STL进修系列之二:尺度模板库(STL)先容
当前位置:以往代写 > C/C++ 教程 >STL进修系列之二:尺度模板库(STL)先容
2019-06-13

STL进修系列之二:尺度模板库(STL)先容

STL进修系列之二:尺度模板库(STL)先容

副标题#e#

此文为转贴文章,由于原文运行在Linux下,我在vc6/7下举办调试,作了部门修改!留意:所有的代码在vc6/vc7下调试通过!尺度模板库(STL)先容

尺度模板库(STL)先容

0 媒介.

1 界说一个list

2 利用list的成员函数push_back和push_front插入一个元素到list中

3 list的成员函数empty()

4 用for轮回来处理惩罚list中的元素

5 用STL的通用算法for_each来处理惩罚list中的元素

6 用STL的通用算法count_if()来统计list中的元素个数

7 利用count_if()的一个越发巨大的函数工具。

8 利用STL通用算法find()在list中查找工具

9 利用STL通用算法find_if()在list中搜索工具

10 利用STL通用算法search在list中找一个序列

11 利用list的成员函数sort()排序一个list。

12 用list的成员函数插入元素到list中

13 List 结构函数

14 利用list成员函数从list中删除元素

15 用list成员函数remove()从list中删除元素。

16 利用STL通用算法remove()从list中删除元素

17 利用STL通用算法stable_partition()和list成员函数splice()来分别一个list

18 结论

在field中利用STL

19 参考书目:


#p#副标题#e#

0 媒介.

这篇文章是关于C++语言的一个新的扩展——尺度模板库的(Standard Template Library),也叫STL。

当我第一次规划写一篇关于STL的文章的时候,我不得不认可我其时低估了这个话题的深度和广度。有许多内容要含盖,也有许多具体描写STL的书。因此我从头思量了一下我本来的想法。我为什么要写这篇文章,又为什么要投稿呢?这会有什麽用呢?有再来一篇关于STL的文章的须要吗?

当我掀开Musser and Saini的页时,我看到了编程时代在我眼前消融。我能看到深夜消失了, 方针软件工程呈现了。我看到了可维护的代码。一年已往了,我利用STL写的软件仍然很容易维护。让人受惊的是其他人可以没有我而维护的很好!

然而,我也记得在一开始的时候很难弄懂那些技能术语。一次,我买了Musser&Saini,每件事都依次呈现,可是在那以前我最盼愿获得的对象是一些好的例子。

当我开始的时候,作为C++一部门的Stroustrup还没出来,它包围了STL。

因此我想写一篇关于一个STL措施员的真实糊口的文章大概会有用。假如我手上有一些好的例子的话,出格是象这样的新题目,我会学的更快。

别的一件事是STL应该很好用。因此,理论上说,我们应该可以顿时开始利用STL。

什麽是STL呢?STL就是Standard Template Library,尺度模板库。这大概是一个汗青上最令人欢快的东西的最无聊的术语。从基础上说,STL是一些“容器”的荟萃,这些“容器”有list, vector,set,map等,STL也是算法和其他一些组件的荟萃。这里的“容器”和算法的荟萃指的是世界上许多智慧人许多年的精品。

STL的目标是尺度化组件,这样你就不消从头开拓它们了。你可以仅仅利用这些现成的组件。STL此刻是C++的一部门,因此不消特别安装什麽。它被内建在你的编译器之内。因为STL的list是一个简朴的容器,所以我规划从它开始先容STL如何利用。假如你分明白这个观念,其他的就都没有问题了。别的, list容器是相当简朴的,我们会看到这一点。

这篇文章中我们将会看到如何界说和初始化一个list,计较它的元素的数量,从一个list里查找元素,删除元素,和一些其他的操纵。要作到这些,我们将会接头两个差异的算法,STL通用算法都是可以操纵不止一个容器的,而list的成员函数是list容器专有的操纵。

这是三类主要的STL组件的简明纲领。STL容器可以生存工具,内建工具和类工具。它们会安详的生存工具,并界说我们可以或许操纵的这个工具的接口。放在蛋架上的鸡蛋不会滚到桌上。它们很安详。因此,在STL容器中的工具也很安详。我知道这个比喻听起来很老土,可是它很正确。

STL算法是尺度算法,我们可以把它们应用在那些容器中的工具上。这些算法都有很著名的执行特性。它们可以给工具排序,删除它们,给它们记数,较量,找出非凡的工具,把它们归并到另一个容器中,以及执行其他有用的操纵。

STL iterator就象是容器中指向工具的指针。STL的算法利用iterator在容器长举办操纵。Iterator配置算法的界线 ,容器的长度,和其他一些工作。举个例子,有些iterator仅让算法读元素,有一些让算法写元素,有一些则两者都行。Iterator也抉择在容器中处理惩罚的偏向。

你可以通过挪用容器的成员函数begin()来获得一个指向一个容器起始位置的iterator。你可以挪用一个容器的 end() 函数来获得已往的最后一个值(就是处理惩罚停在那的谁人值)。

#p#分页标题#e#

这就是STL所有的对象,容器、算法、和答允算法事情在容器中的元素上的iterator。算法以符合、尺度的要领操纵工具,并可通过iterator获得容器准确的长度。一旦做了这些,它们就在也不会“跑出界线”。尚有一些其他的对这些焦点组件范例有成果性加强的组件,譬喻函数工具。我们将会看到有关这些的例子,此刻 ,我们先来看一看STL的list。

1 界说一个list

我们可以象这样来界说一个STL的list:

#include <string>
#include <list>
using namespace std;
int main (void)
{
 list<string> Milkshakes;
 return 0;
}

这就行了,你已经界说了一个list。简朴吗?list<string> Milkshakes这句是你声明白list<string>模板类 的一个实例,然后就是实例化这个类的一个工具。可是我们别急着做这个。在这一步其实你只需要知道你界说了 一个字符串的list。你需要包括提供STL list类的头文件。我用gcc 2.7.2在我的Linux上编译这个测试措施,譬喻:

g++ test1.cpp -o test1

留意iostream.h这个头文件已经被STL的头文件放弃了。这就是为什么这个例子中没有它的原因。

此刻我们有了一个list,我们可以看实利用它来装对象了。我们将把一个字符串加到这个list里。有一个很是 重要的对象叫做list的值范例。值范例就是list中的工具的范例。在这个例子中,这个list的值范例就是字符串,string , 这是因为这个list用来放字符串。

#p#副标题#e#

2 利用list的成员函数push_back和push_front插入一个元素到list中

#include <string>
#include <list>
using namespace std;
int main (void)
{
 list<string> Milkshakes;
 Milkshakes.push_back("Chocolate");
 Milkshakes.push_back("Strawberry");
 Milkshakes.push_front("Lime");
 Milkshakes.push_front("Vanilla");
 return 0;
}

我们此刻有个4个字符串在list中。list的成员函数push_back()把一个工具放到一个list的后头,而 push_front()把工具放到前面。我凡是把一些错误信息push_back()到一个list中去,然后push_front()一个标题到list中, 这样它就会在这个错误动静以前打印它了。

3 list的成员函数empty()

知道一个list是否为空很重要。假如list为空,empty()这个成员函数返回真。我凡是会这样利用它。通篇措施我都用push_back()来把错误动静放到list中去。然后,通过挪用empty() 我就可以说出这个措施是否陈诉了错误。假如我界说了一个list来放信息,一个放告诫,一个放严重错误, 我就可以通过利用empty()等闲的说出到底有那种范例的错误产生了。

我可以整理这些list,然后在打印它们之前,用标题来整理它们,可能把它们排序成类。

这是我的意思:

/*
|| Using a list to track and report program messages and status
*/
//不要利用#include <iostream.h>
#include <iostream>
#include <string>
#include <list>
using namespace std;
int main (void)
{
 #define OK 0
 #define INFO 1
 #define WARNING 2
 int return_code;
 list<string> InfoMessages;
 list<string> WarningMessages;
 // during a program these messages are loaded at various points
 InfoMessages.push_back("Info: Program started");
 // do work...
 WarningMessages.push_back("Warning: No Customer records have been found");
 // do work...
 return_code = OK;
 if (!InfoMessages.empty()) {     // there were info messages
   InfoMessages.push_front("Informational Messages:");
   // ... print the info messages list, we'll see how later
   return_code = INFO;
 }
 if (!WarningMessages.empty()) {    // there were warning messages
   WarningMessages.push_front("Warning Messages:");
   // ... print the warning messages list, we'll see how later
   return_code = WARNING;
 }
 // If there were no messages say so.
 if (InfoMessages.empty() && WarningMessages.empty()) {
   cout << "There were no messages " << endl;
 }
 return return_code;
}

#p#副标题#e#

4 用for轮回来处理惩罚list中的元素

我们想要遍历一个list,好比打印一其中的所有工具来看看list上差异操纵的功效。要一个元素一个元素的遍历一个list, 我们可以这样做:

#p#分页标题#e#

/*
|| How to print the contents of a simple STL list. Whew!
*/
//不要利用#include <iostream.h>
#include <iostream>
#include <string>
#include <list>
using namespace std;
void main (void)
{
 list<string> Milkshakes;
 list<string>::iterator MilkshakeIterator;
 Milkshakes.push_back("Chocolate");
 Milkshakes.push_back("Strawberry");
 Milkshakes.push_front("Lime");
 Milkshakes.push_front("Vanilla");

 // print the milkshakes
 Milkshakes.push_front("The Milkshake Menu");
 Milkshakes.push_back("*** Thats the end ***");
 for (MilkshakeIterator=Milkshakes.begin();
     MilkshakeIterator!=Milkshakes.end();
     ++MilkshakeIterator)
 {
  // dereference the iterator to get the element
  cout << *MilkshakeIterator << endl;
 }  
}

这个措施界说了一个iterator,MilkshakeIterator。我们把它指向了这个list的第一个元素。这可以挪用Milkshakes.begin()来作到,它会返回一个指向list开头的iterator。然后我们把它和Milkshakes.end()的 返回值来做较量,当我们到了那儿的时候就停下来。

容器的end()函数会返回一个指向容器的最后一个位置的iterator。当我们到了哪里,就遏制操纵。我们不能不理容器的end()函数的返回值。我们仅知道它意味着已经处理惩罚到了这个容器的末端,应该遏制处理惩罚了。所有的STL容器都要这样做。

在上面的例子中,每一次执行for轮回,我们就反复引用iterator来获得我们打印的字符串。

在STL编程中,我们在每个算法中都利用一个或多个iterator。我们利用它们来存取容器中的工具。要存取一个给定的工具,我们把一个iterator指向它,然后间接引用这个iterator。

这个list容器,就象你所想的,它不支持在iterator加一个数来指向隔一个的工具。就是说,我们不能用Milkshakes.begin()+2来指向list中的第三个工具,因为STL的list是以双链的list来实现的, 它不支持随机存取。vector和deque(向量和双端行列)和一些其他的STL的容器可以支持随机存取。

上面的措施打印出了list中的内容。任何人读了它都能顿时大白它是怎麽事情的。它利用尺度的iterator和尺度 的list容器。没有几多措施员依赖它内里装的对象, 仅仅是尺度的C++。这是一个向前的重要步调。这个例子利用STL使我们的软件越发尺度。

5 用STL的通用算法for_each来处理惩罚list中的元素

利用STL list和 iterator,我们要初始化、较量和给iterator增量来遍历这个容器。STL通用的for_each 算法可以或许减轻我们的事情。

/*
|| How to print a simple STL list MkII
*/
//不要利用#include <iostream.h>
#include <iostream>
#include <string>
#include <list>
#include <algorithm>
using namespace std;
PrintIt (string& StringToPrint) {
 cout << StringToPrint << endl;
}
void main (void) {
 list<string> FruitAndVegetables;
 FruitAndVegetables.push_back("carrot");
 FruitAndVegetables.push_back("pumpkin");
 FruitAndVegetables.push_back("potato");
 FruitAndVegetables.push_front("apple");
 FruitAndVegetables.push_front("pineapple");
 for_each (FruitAndVegetables.begin(), FruitAndVegetables.end(), PrintIt);
}

#p#副标题#e#

在这个措施中我们利用STL的通用算法for_each()来遍历一个iterator的范畴,然后挪用PrintIt()来处理惩罚每个工具。我们不需要初始化、较量和给iterator增量。for_each()为我们大度的完成了这些事情。我们执行于工具上的 操纵被很好的打包在这个函数以外了,我们不消再做那样的轮回了,我们的代码越发清晰了。

for_each算法引用了iterator范畴的观念,这是一个由起始iterator和一个末端iterator指出的范畴。起始iterator指出操纵由那边开始,末端iterator指明到哪竣事,可是它不包罗在这个范畴内。用STL的通用算法count()来统计list中的元素个数。

STL的通用算法count()和count_it()用来给容器中的工具记数。就象for_each()一样,count()和count_if() 算法也是在iterator范畴内来做的。

让我们在一个学生考试后果的list中来数一数满分的个数。这是一个整型的List。

/*
|| How to count objects in an STL list
*/
#include <list>
#include <algorithm>
#include <iostream>
using namespace std;
void main (void)
{
 list<int> Scores;
 Scores.push_back(100); Scores.push_back(80);
 Scores.push_back(45); Scores.push_back(75);
 Scores.push_back(99); Scores.push_back(100);
 int NumberOf100Scores(0);
 //此行有错count (Scores.begin(), Scores.end(), 100, NumberOf100Scores);
 NumberOf100Scores=count (Scores.begin(), Scores.end(), 100)
 cout << "There were " << NumberOf100Scores << " scores of 100" << endl;
}

#p#分页标题#e#

count()算法统计便是某个值的工具的个数。上面的例子它查抄list中的每个整型工具是不是100。每次容器中的工具便是100,它就给NumberOf100Scores加1。这是措施的输出:

There were 2 scores of 100

zbf讲明:count的算法如下

template<class _II, class _Ty> inline

_CNTSIZ(_II) count(_II _F, _II _L, const _Ty& _V)

{_CNTSIZ(_II) _N = 0;

for (; _F != _L; ++_F)

if (*_F == _V)

++_N;

return (_N); }

#p#副标题#e#

6 用STL的通用算法count_if()来统计list中的元素个数

count_if()是count()的一个更有趣的版本。他回收了STL的一个新组件,函数工具。count_if() 带一个函数工具的参数。函数工具是一个至少带有一个operator()要领的类。有些STL算法作为参数吸收 函数工具并挪用这个函数工具的operator()要领。

函数工具被约定为STL算法挪用operator时返回true或false。它们按照这个来鉴定这个函数。举个例子会 说的更清楚些。count_if()通过通报一个函数工具来作出比count()越发巨大的评估以确定一个工具是否应该被 记数。在这个例子里我们将数一数牙刷的销售数量。我们将提交包括四个字符的销售码和产物说明的销售记录。

/*
|| Using a function object to help count things
*/
#include <string>
#include <list>
#include <algorithm>
#include <iostream>
using namespace std;
const string ToothbrushCode("0003");
class IsAToothbrush
{
public: 
  bool operator() ( string& SalesRecord )
  {
   return SalesRecord.substr(0,4)==ToothbrushCode;
  }  
};
void main (void)
{
 list<string> SalesRecords;
 SalesRecords.push_back("0001 Soap");
 SalesRecords.push_back("0002 Shampoo");
 SalesRecords.push_back("0003 Toothbrush");
 SalesRecords.push_back("0004 Toothpaste");
 SalesRecords.push_back("0003 Toothbrush");

 int NumberOfToothbrushes(0);
 NumberOfToothbrushes=count_if (SalesRecords.begin(), SalesRecords.end(),
       IsAToothbrush());
 cout << "There were "
    << NumberOfToothbrushes
    << " toothbrushes sold" << endl;
}

这是这个措施的输出:

There were 2 toothbrushes sold

这个措施是这样事情的:界说一个函数工具类IsAToothbrush,这个类的工具能判定出卖出的是否是牙刷 。假如这个记录是卖出牙刷的记录的话,函数挪用operator()返回一个true,不然返回false。

count_if()算法由第一和第二两个iterator参数指出的范畴来处理惩罚容器工具。它将对每个 IsAToothbrush?()返回true的容器中的工具增加NumberOfToothbrushes的值。

最后的功效是NumberOfToothbrushes这个变量生存了产物代码域为"0003"的记录的个数,也就是牙刷的个数。

留意count_if()的第三个参数IsAToothbrush(),它是由它的结构函数姑且结构的一个工具。你可以把IsAToothbrush类的一个姑且工具 通报给count_if()函数。count_if()将对该容器的每个工具挪用这个函数。

#p#副标题#e#

7 利用count_if()的一个越发巨大的函数工具。

我们可以更进一步的研究一下函数工具。假设我们需要通报更多的信息给一个函数工具。我们不能通过 挪用operator来作到这点,因为必需界说为一个list的中的工具的范例。然而我们通过为IsAToothbrush指出一个非缺省的结构函数就可以用任何我们所需要的信息来初始化它了。譬喻,我们大概需要每个牙刷有一个不定的代码。我们可以把这个信息加到下面的函数工具中:

/*
|| Using a more complex function object
*/
#include <iostream>
#include <string>
#include <list>
#include <algorithm>
using namespace std;
class IsAToothbrush
{
public:
 IsAToothbrush(string& InToothbrushCode) :
   ToothbrushCode(InToothbrushCode) {}
 bool operator() (string& SalesRecord)
 {
  return SalesRecord.substr(0,4)==ToothbrushCode;
 }   
private:
 string ToothbrushCode;
};
void main (void)
{
 list<string> SalesRecords;
 SalesRecords.push_back("0001 Soap");
 SalesRecords.push_back("0002 Shampoo");
 SalesRecords.push_back("0003 Toothbrush");
 SalesRecords.push_back("0004 Toothpaste");
 SalesRecords.push_back("0003 Toothbrush");

 string VariableToothbrushCode("0003");
 int NumberOfToothbrushes(0);
 NumberOfToothbrushes=count_if (SalesRecords.begin(), SalesRecords.end(),
       IsAToothbrush(VariableToothbrushCode));
 cout << "There were "
    << NumberOfToothbrushes
    << " toothbrushes matching code "
    << VariableToothbrushCode
    << " sold"
    << endl;
}

措施的输出是:

There were 2 toothbrushes matching code 0003 sold

这个例子演示了如何向函数工具通报信息。你可以界说任意你想要的结构函数,你可以在函数工具中做任何你 想做的处理惩罚,都可以正当编译通过。

你可以看到函数工具真的扩展了根基记数算法。

到此刻为止,我们都进修了:

界说一个list

向list中插手元素

如何知道list是否为空

如何利用for轮回来遍历一个list

如何利用STL的通用算法for_each来遍历list

list成员函数begin() 和 end() 以及它们的意义

iterator范畴的观念和一个范畴的最后一个位置实际上并不被处理惩罚这一事实

如何利用STL通用算法count()和count_if()来对一个list中的工具记数

如何界说一个函数工具

#p#分页标题#e#

我选用这些例子来演示list的一般操纵。假如你懂了这些根基道理,你就可以毫无疑问的利用STL了 发起你作一些操练。我们此刻用一些越发巨大的操纵来扩展我们的常识,包罗list成员函数和STL通用算法。

#p#副标题#e#

8 利用STL通用算法find()在list中查找工具

我们如安在list中查找对象呢?STL的通用算法find()和find_if()可以做这些。就象for_each(), count(), count_if() 一样,这些算法也利用iterator范畴,这个范畴指出一个list或任意 其他容器中的一部门来处理惩罚。凡是首iterator指着开始的位置,次iterator指着遏制处理惩罚的处所。由次iterator指出的元素不被处理惩罚。这是find()如何事情:

/*
|| How to find things in an STL list
*/
#include <string>
#include <list>
#include <algorithm>
#include <iostream>
using namespace std;
void main (void) {
  list<string> Fruit;
  list<string>::iterator FruitIterator;
  Fruit.push_back("Apple");
  Fruit.push_back("Pineapple");
  Fruit.push_back("Star Apple");
  FruitIterator = find (Fruit.begin(), Fruit.end(), "Pineapple");
  if (FruitIterator == Fruit.end()) {
    cout << "Fruit not found in list" << endl;
  }
  else {
    cout << *FruitIterator << endl;
  }
}

输出是:

Pineapple

假如没有找到指出的工具,就会返回Fruit.end()的值,要是找到了就返回一个指着找到的工具的iterator

9 利用STL通用算法find_if()在list中搜索工具

这是find()的一个更强大的版本。这个例子演示了find_if(),它吸收一个函数工具的参数作为参数, 并利用它来做更巨大的评价工具是否和给出的查找条件相付。假设我们的list中有一些按年月分列的包括了事件和日期的记录。我们但愿找出产生在1997年的事件。

/*
|| How to find things in an STL list MkII
*/
#include <string>
#include <list>
#include <algorithm>
class EventIsIn1997 {
public:
  bool operator () (string& EventRecord) {
    // year field is at position 12 for 4 characters in EventRecord
    return EventRecord.substr(12,4)=="1997";
  }
};
void main (void) {
  list<string> Events;
  // string positions 0123456789012345678901234567890123456789012345
  Events.push_back("07 January 1995 Draft plan of house prepared");
  Events.push_back("07 February 1996 Detailed plan of house prepared");
  Events.push_back("10 January 1997 Client agrees to job");
  Events.push_back("15 January 1997 Builder starts work on bedroom");
  Events.push_back("30 April  1997 Builder finishes work");
  list<string>::iterator EventIterator = find_if (Events.begin(), Events.end(), EventIsIn1997());
  // find_if completes the first time EventIsIn1997()() returns true
  // for any object. It returns an iterator to that object which we
  // can dereference to get the object, or if EventIsIn1997()() never
  // returned true, find_if returns end()
  if (EventIterator==Events.end()) {
    cout << "Event not found in list" << endl;
  }
  else {
    cout << *EventIterator << endl;
  }
}

这是措施的输出:

10 January 1997 Client agrees to job

#p#副标题#e#

10 利用STL通用算法search在list中找一个序列

一些字符在STL容器中很长处理惩罚,让我们看一看一个难处理惩罚的字符序列。我们将界说一个list来放字符。

list<char> Characters;

#p#分页标题#e#

此刻我们有了一个字符序列,它不消任何辅佐就知道然后打点内存。它知道它是从那边开始、到那边竣事。它很是有用。我不知道我是否说过以null末了的字符数组。

让我们插手一些我们喜欢的字符到这个list中:

Characters.push_back('\0');
Characters.push_back('\0');
Characters.push_back('1');
Characters.push_back('2');

我们将获得几多个空字符呢?

int NumberOfNullCharacters(0);
count(Characters.begin(), Characters.end(), '\0', NumberOfNullCharacters);
cout << "We have " << NumberOfNullCharacters << endl;

让我们找字符’1′

list<char>::iterator Iter;
Iter = find(Characters.begin(), Characters.end(), '1');
cout << "We found " << *Iter << endl;

这个例子演示了STL容器答允你以更尺度的要领来处理惩罚空字符。此刻让我们用STL的search算法来搜索容器中 的两个null。就象你猜的一样,STL通用算法search()用来搜索一个容器,可是是搜索一个元素串,不象find()和find_if() 只搜索单个的元素。

/*
|| How to use the search algorithm in an STL list
*/
#include <string>
#include <list>
#include <algorithm>
#include <iostream>
using namespace std;
void main ( void){
  list<char> TargetCharacters;
  list<char> ListOfCharacters;
  TargetCharacters.push_back('\0');
  TargetCharacters.push_back('\0');
  ListOfCharacters.push_back('1');
  ListOfCharacters.push_back('2');
  ListOfCharacters.push_back('\0');
  ListOfCharacters.push_back('\0');
  list<char>::iterator PositionOfNulls = search(ListOfCharacters.begin(), ListOfCharacters.end(), TargetCharacters.begin(), TargetCharacters.end());
  if (PositionOfNulls!=ListOfCharacters.end())
    cout << "We found the nulls" << endl;
}

The output of the program will be 这是措施的输出:

We found the nulls

search算法在一个序列中找另一个序列的第一次呈现的位置。在这个例子里我们在ListOfCharacters中 找TargetCharacters这个序列的第一次呈现,TargetCharacters是包括两个null字符的序列。search的参数是两个指着查找方针的iterator和两个指着搜索范畴的iterators。因此我们我们在整个的ListOfCharacters的范畴内查找TargetCharacters这个list的整个序列。—www.bianceng.cn

假如TargetCharacters被发明,search就会返回一个指着ListOfCharacters中序列匹配的第一个 字符的iterator。假如没有找到匹配项,search返回ListOfCharacters.end()的值。

#p#副标题#e#

11 利用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>
#include <iostream>
using namespace std;
PrintIt (string& StringToPrint) { cout << StringToPrint << endl;}
void 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
12 用list的成员函数插入元素到list中

list的成员函数push_front()和push_back()别离把元素插手到list的前面和后头。你可以利用insert() 把工具插入到list中的任那里所。

insert()可以插手一个工具,一个工具的若干份拷贝,可能一个范畴以内的工具。这里是一些 插入工具到list中的例子:

#p#分页标题#e#

/*
|| Using insert to insert elements into a list.
*/
#include <string>
#include <list>
#include <algorithm>
#include <iostream>
using namespace std;
void main (void) {
  list<int> list1;
  list<int> ::iterator List1Itetator;
  /*
  || 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
  */
  //output
  for(List1Itetator=list1.begin();List1Itetator!=list1.end();
   List1Itetator++)
  {
   cout<<*List1Itetator<<endl;
  }
}

留意,insert()函数把一个或若干个元素插入到你指出的iterator的位置。你的元素将呈此刻 iterator指出的位置以前。

#p#副标题#e#

13 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
14 利用list成员函数从list中删除元素

list成员函数pop_front()删掉list中的第一个元素,pop_back()删掉最后一个元素。函数erase()删掉由一个iterator指出的元素。尚有另一个erase()函数可以删掉一个范畴的元素。

/*
|| Erasing objects from a list
*/
#include <list>
#include <string>
#include <iostream>
#include <algorithm>
using namespace std;
void   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

15 用list成员函数remove()从list中删除元素。

list的成员函数remove()用来从list中删除元素。

#p#分页标题#e#

/*
|| Using the list member function remove to remove elements
*/
#include <string>
#include <list>
#include <algorithm>
#include<iostream>
using namespace std;
PrintIt (const string& StringToPrint) {
  cout << StringToPrint << endl;
}
void 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

#p#副标题#e#

16 利用STL通用算法remove()从list中删除元素

通用算法remove()利用和list的成员函数差异的方法事情。一般环境下不改变容器的巨细。

/*
|| Using the generic remove algorithm to remove list elements
*/
#include <string>
#include <list>
#include <algorithm>
#include <iostream>
using namespace std;
PrintIt(string& AString) { cout << AString << endl; }
void 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);//zbf.不能用此种要领打印
}
输出功效将为:
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函数来删除重新末了到老末了的部门。

#p#副标题#e#

17 利用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>
#include <iostream>
using namespace std;
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";
  }
};
void 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
}

给出这样的呼吁行:

//zbf:在vc6IDE下可以模仿出一样的功效

test17 -w linux -o is -w great

输出是:

#p#分页标题#e#

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

#p#副标题#e#

18 结论

我们仅仅简朴的谈了谈你可以用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写过几个贸易措施。它在许多方面淘汰了我的事情量,也解除了许多逻辑错误。

#p#分页标题#e#

最大的一个措施有约莫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的教科书。去看看,也许你能发明什么。

19 参考书目:

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

    关键字:

在线提交作业