|
楼主 |
发表于 2003-6-3 22:13:21
|
显示全部楼层
使用list的成员函数sort()排序一个list。
要排序一个list,我们要用list的成员函数sort(),而不是通用算法sort()。所有我们用过的算法都是 通用算法。然而,在STL中有时容器支持它自己对一个特殊算法的实现,这通常是为了提高性能。
在这个例子中,list容器有它自己的sort算法,这是因为通用算法仅能为那些提供随机存取里面元素 的容器排序,而由于list是作为一个连接的链表实现的,它不支持对它里面的元素随机存取。所以就需要一个特殊的 sort()成员函数来排序list。
由于各种原因,容器在性能需要较高或有特殊效果需求的场合支持外部函数(extra functions), 这通过利用构造函数的结构特性可以作到。
- /*
- || How to sort an STL list
- */
- #include <string>
- #include <list>
- #include <algorithm>
- #
- PrintIt (string& StringToPrint) { cout << StringToPrint << endl;}
- #
- int main (void) {
- list<string> Staff;
- list<string>::iterator PeopleIterator;
- #
- Staff.push_back("John");
- Staff.push_back("Bill");
- Staff.push_back("Tony");
- Staff.push_back("Fidel");
- Staff.push_back("Nelson");
- #
- cout << "The unsorted list " << endl;
- for_each(Staff.begin(), Staff.end(), PrintIt );
- #
- Staff.sort();
- #
- cout << "The sorted list " << endl;
- for_each(Staff.begin(), Staff.end(), PrintIt);
- }
复制代码
输出是:
The unsorted list
John
Bill
Tony
Fidel
Nelson
The sorted list
Bill
Fidel
John
Nelson
Tony
用list的成员函数插入元素到list中
list的成员函数push_front()和push_back()分别把元素加入到list的前面和后面。你可以使用insert() 把对象插入到list中的任何地方。
insert()可以加入一个对象,一个对象的若干份拷贝,或者一个范围以内的对象。这里是一些 插入对象到list中的例子:
- /*
- || Using insert to insert elements into a list.
- */
- #include <list>
- #
- int main (void) {
- list<int> list1;
- #
- /*
- || Put integers 0 to 9 in the list
- */
- for (int i = 0; i < 10; ++i) list1.push_back(i);
- #
- /*
- || Insert -1 using the insert member function
- || Our list will contain -1,0,1,2,3,4,5,6,7,8,9
- */
- list1.insert(list1.begin(), -1);
- #
- /*
- || Insert an element at the end using insert
- || Our list will contain -1,0,1,2,3,4,5,6,7,8,9,10
- */
- list1.insert(list1.end(), 10);
- #
- /*
- || Inserting a range from another container
- || Our list will contain -1,0,1,2,3,4,5,6,7,8,9,10,11,12
- */
- int IntArray[2] = {11,12};
- list1.insert(list1.end(), &IntArray[0], &IntArray[2]);
- #
- /*
- || As an exercise put the code in here to print the lists!
- || Hint: use PrintIt and accept an interger
- */
- }
复制代码
注意,insert()函数把一个或若干个元素插入到你指出的iterator的位置。你的元素将出现在 iterator指出的位置以前。
List 构造函数
我们已经象这样定义了list:
list<int> Fred;
你也可以象这样定义一个list,并同时初始化它的元素:
// define a list of 10 elements and initialise them all to 0
list<int> Fred(10, 0);
// list now contains 0,0,0,0,0,0,0,0,0,0
或者你可以定义一个list并用另一个STL容器的一个范围来初始化它,这个STL容器不一定是一个list, 仅仅需要是元素类型相同的的容器就可以。
vector<int> Harry;
Harry.push_back(1);
Harry.push_back(2);
#
// define a list and initialise it with the elements in Harry
list<int> Bill(Harry.begin(), Harry.end());
// Bill now contains 1,2
使用list成员函数从list中删除元素
list成员函数pop_front()删掉list中的第一个元素,pop_back()删掉最后一个元素。 函数erase()删掉由一个iterator指出的元素。还有另一个erase()函数可以删掉一个范围的元素。
- /*
- || Erasing objects from a list
- */
- #include <list>
- #
- int main (void) {
- list<int> list1; // define a list of integers
- #
- /*
- || Put some numbers in the list
- || It now contains 0,1,2,3,4,5,6,7,8,9
- */
- for (int i = 0; i < 10; ++i) list1.push_back(i);
- #
- list1.pop_front(); // erase the first element 0
- #
- list1.pop_back(); // erase the last element 9
- #
- list1.erase(list1.begin()); // erase the first element (1) using an iterator
- #
- list1.erase(list1.begin(), list1.end()); // erase all the remaining elements
- #
- cout << "list contains " << list1.size() << " elements" << endl;
- }
复制代码
输出是:
list contains 0 elements
用list成员函数remove()从list中删除元素。
list的成员函数remove()用来从list中删除元素。
- /*
- || Using the list member function remove to remove elements
- */
- #include <string>
- #include <list>
- #include <algorithm>
- #
- PrintIt (const string& StringToPrint) {
- cout << StringToPrint << endl;
- }
- #
- int main (void) {
- list<string> Birds;
- #
- Birds.push_back("cockatoo");
- Birds.push_back("galah");
- Birds.push_back("cockatoo");
- Birds.push_back("rosella");
- Birds.push_back("corella");
- #
- cout << "Original list with cockatoos" << endl;
- for_each(Birds.begin(), Birds.end(), PrintIt);
- #
- Birds.remove("cockatoo");
- #
- cout << "Now no cockatoos" << endl;
- for_each(Birds.begin(), Birds.end(), PrintIt);
-
- }
复制代码
输出是:
Original list with cockatoos
cockatoo
galah
cockatoo
rosella
corella
Now no cockatoos
galah
rosella
corella
使用STL通用算法remove()从list中删除元素
通用算法remove()使用和list的成员函数不同的方式工作。一般情况下不改变容器的大小。
- /*
- || Using the generic remove algorithm to remove list elements
- */
- #include <string>
- #include <list>
- #include <algorithm>
- #
- PrintIt(string& AString) { cout << AString << endl; }
- #
- int main (void) {
- list<string> Birds;
- list<string>::iterator NewEnd;
- #
- Birds.push_back("cockatoo");
- Birds.push_back("galah");
- Birds.push_back("cockatoo");
- Birds.push_back("rosella");
- Birds.push_back("king parrot");
- #
- cout << "Original list" << endl;
- for_each(Birds.begin(), Birds.end(), PrintIt);
- #
- NewEnd = remove(Birds.begin(), Birds.end(), "cockatoo");
- #
- cout << endl << "List according to new past the end iterator" << endl;
- for_each(Birds.begin(), NewEnd, PrintIt);
- #
- cout << endl << "Original list now. Care required!" << endl;
- for_each(Birds.begin(), Birds.end(), PrintIt);
- }
复制代码
The output will be
Original list
cockatoo
galah
cockatoo
rosella
king parrot
List according to new past the end iterator
galah
rosella
king parrot
Original list now. Care required!
galah
rosella
king parrot
rosella
king parrot
通用remove()算法返回一个指向新的list的结尾的iterator。从开始到这个新的结尾(不含新结尾元素)的范围 包含了remove后剩下所有元素。你可以用list成员函数erase函数来删除从新结尾到老结尾的部分。
使用STL通用算法stable_partition()和list成员函数splice()来划分一个list
我们将完成一个稍微有点复杂的例子。它演示STL通用算法stable_partition()算法和一个list成员函数 splice()的变化。注意函数对象的使用和没有使用循环。 通过简单的语句调用STL算法来控制。
stable_partition()是一个有趣的函数。它重新排列元素,使得满足指定条件的元素排在 不满足条件的元素前面。它维持着两组元素的顺序关系。
splice 把另一个list中的元素结合到一个list中。它从源list中删除元素。
在这个例子中,我们想从命令行接收一些标志和四个文件名。文件名必须'按顺序出现。通过使用stable_partition() 我们可以接收和文件名混为任何位置的标志,并且不打乱文件名的顺序就把它们放到一起。
由于记数和查找算法都很易用,我们调用这些算法来决定哪个标志被设置而哪个标志未被设置。 我发现容器用来管理少量的象这样的动态数据。
- /*
- || Using the STL stable_partition algorithm
- || Takes any number of flags on the command line and
- || four filenames in order.
- */
- #include <string>
- #include <list>
- #include <algorithm>
- #
- PrintIt ( string& AString ) { cout << AString << endl; }
- #
- class IsAFlag {
- public:
- bool operator () (string& PossibleFlag) {
- return PossibleFlag.substr(0,1)=="-";
- }
- };
- #
- class IsAFileName {
- public:
- bool operator () (string& StringToCheck) {
- return !IsAFlag()(StringToCheck);
- }
- };
- #
- class IsHelpFlag {
- public:
- bool operator () (string& PossibleHelpFlag) {
- return PossibleHelpFlag=="-h";
- }
- };
- #
- int main (int argc, char *argv[]) {
- #
- list<string> CmdLineParameters; // the command line parameters
- list<string>::iterator StartOfFiles; // start of filenames
- list<string> Flags; // list of flags
- list<string> FileNames; // list of filenames
- #
- for (int i = 0; i < argc; ++i) CmdLineParameters.push_back(argv[i]);
- #
- CmdLineParameters.pop_front(); // we don't want the program name
- #
- // make sure we have the four mandatory file names
- int NumberOfFiles(0);
- count_if(CmdLineParameters.begin(), CmdLineParameters.end(),
- IsAFileName(), NumberOfFiles);
- #
- cout << "The "
- << (NumberOfFiles == 4 ? "correct " : "wrong ")
- << "number ("
- << NumberOfFiles
- << ") of file names were specified" << endl;
- #
- // move any flags to the beginning
- StartOfFiles =
- stable_partition(CmdLineParameters.begin(), CmdLineParameters.end(),
- IsAFlag());
- #
- cout << "Command line parameters after stable partition" << endl;
- for_each(CmdLineParameters.begin(), CmdLineParameters.end(), PrintIt);
- #
- // Splice any flags from the original CmdLineParameters list into Flags list.
- Flags.splice(Flags.begin(), CmdLineParameters,
- CmdLineParameters.begin(), StartOfFiles);
- #
- if (!Flags.empty()) {
- cout << "Flags specified were:" << endl;
- for_each(Flags.begin(), Flags.end(), PrintIt);
- }
- else {
- cout << "No flags were specified" << endl;
- }
- #
- // parameters list now contains only filenames. Splice them into FileNames list.
- FileNames.splice(FileNames.begin(), CmdLineParameters,
- CmdLineParameters.begin(), CmdLineParameters.end());
- #
- if (!FileNames.empty()) {
- cout << "Files specified (in order) were:" << endl;
- for_each(FileNames.begin(), FileNames.end(), PrintIt);
- }
- else {
- cout << "No files were specified" << endl;
- }
- #
- // check if the help flag was specified
- if (find_if(Flags.begin(), Flags.end(), IsHelpFlag())!=Flags.end()) {
- cout << "The help flag was specified" << endl;
- }
- #
- // open the files and do whatever you do
- #
- }
复制代码
给出这样的命令行:
test17 -w linux -o is -w great
输出是:
The wrong number (3) of file names were specified
Command line parameters after stable partition
-w
-o
-w
linux
is
great
Flags specified were:
-w
-o
-w
Files specified (in order) were:
linux
is
great
结论
我们仅仅简单的谈了谈你可以用list做的事情。我们没有说明一个对象的用户定义类,虽然这个不难。
如果你懂了刚才说的这些算法背后的概念,那么你使用剩下的那些算法就应该没有问题了。使用STL 最重要的东西就是得到基本理论。
STL的关键实际上是iterator。STL算法作为参数使用iterator,他们指出一个范围,有时是一个范围, 有时是两个。STL容器支持iterator,这就是为什么我们说 list<int>::iterator, 或 list<char>::iterator, 或 list<string>::iterator.
iterator有很好的定义继承性。它们非常有用。某些iterator仅支持对一个容器只读,某些 仅支持写,还有一些仅能向前指,有一些是双向的。有一些iterator支持对一个容器的随机存取。
STL算法需要某个iterator作为"动力" 如果一个容器不提供iterator作为"动力",那么这个算法将无法编译。例如,list容器仅提供双向的 iterator。通常的sort()算法需要随机存取的iterator。这就是为什么我们需要一个特别的list成员函数sort()。
要合适的实际使用STL,你需要仔细学习各种不同的iterator。你需要知道每种容器都支持那类iterator。 你还需要知道算法需要那种iterator,你当然也需要懂得你可以有那种iterator。
在field中使用STL
去年,我曾用STL写过几个商业程序。它在很多方面减少了我的工作量,也排除了很多逻辑错误。
最大的一个程序有大约5000行。可能最惊人的事情就是它的速度。它读入并处理一个1-2兆的 报告文件仅花大约20秒。我是在linux上用gcc2.7.2开发的,现在运行在HP-UX机器上。 它一共用了大约50和函数对象和很多容器,这些容器的大小从小list到一个有14,000个元素的map都有。
一个程序中的函数对象是处于一个继承树中,顶层的函数对象调用低层的函数对象。我大量的使用STL算法for_each() ,find(),find_if(),count()和count_if(),我尽量减少使用程序内部的函数,而使用STL的算法调用。
STL倾向于自动的把代码组织成清晰的控制和支持模块。通过小心使用函数对象并给它们 起有意义的名字,我使它们在我的软件的控制流中流动。
还有很多关于STL编程要知道的东西,我希望你通过这些例子可以愉快的工作。
参考数目中的两本书在web上都有勘误表,你可以自己改正它们。
Stroustrup在每一章后面都有个建议栏,特别是对于出学者有用。正本书比早期的版本更加健谈。 它也更大了。书店里还可以找到其他几本关于STL的教科书。去看看,也许你能发现什么。
参考书目
The STL Tutorial and Reference Guide, David Musser and Atul Saini. Addison Wesley 1996. 《STL教程和参考手册》
The C++ Programming Language 3e, Bjarne Stroustrup. Addison Wesley 1997.
|
|