STL 简介

来源:互联网 发布:小程序做淘宝客怎么样 编辑:程序博客网 时间:2024/06/05 10:16

一、STL简介
STL(Standard Template Library,标准模板库)是惠普实验室开发的一系列软件的统称。它是由Alexander Stepanov、Meng Lee和David R Musser在惠普实验室工作时所开发出来的。现在虽说它主要出现在C++中,但在被引入C++之前该技术就已经存在了很长的一段时间。

STL的代码从广义上讲分为三类:algorithm(算法)、container(容器)和iterator(迭代器),几乎所有的代码都采用了模板类和模版函数的方式,这相比于传统的由函数和类组成的库来说提供了更好的代码重用机会。在C++标准中,STL被组织为下面的13个头文件:<algorithm>、<deque>、<functional>、<iterator>、<vector>、<list>、<map>、<memory>、<numeric>、<queue>、<set>、<stack>和<utility>。以下笔者就简单介绍一下STL各个部分的主要特点。

二、算法
大家都能取得的一个共识是函数库对数据类型的选择对其可重用性起着至关重要的作用。举例来说,一个求方根的函数,在使用浮点数作为其参数类型的情况下的可重用性肯定比使用整型作为它的参数类性要高。而C++通过模板的机制允许推迟对某些类型的选择,直到真正想使用模板或者说对模板进行特化的时候,STL就利用了这一点提供了相当多的有用算法。它是在一个有效的框架中完成这些算法的——你可以将所有的类型划分为少数的几类,然后就可以在模版的参数中使用一种类型替换掉同一种类中的其他类型。

STL提供了大约100个实现算法的模版函数,比如算法for_each将为指定序列中的每一个元素调用指定的函数,stable_sort以你所指定的规则对序列进行稳定性排序等等。这样一来,只要我们熟悉了STL之后,许多代码可以被大大的化简,只需要通过调用一两个算法模板,就可以完成所需要的功能并大大地提升效率。

算法部分主要由头文件<algorithm>,<numeric>和<functional>组成。<algorithm>是所有STL头文件中最大的一个(尽管它很好理解),它是由一大堆模版函数组成的,可以认为每个函数在很大程度上都是独立的,其中常用到的功能范围涉及到比较、交换、查找、遍历操作、复制、修改、移除、反转、排序、合并等等。<numeric>体积很小,只包括几个在序列上面进行简单数学运算的模板函数,包括加法和乘法在序列上的一些操作。<functional>中则定义了一些模板类,用以声明函数对象。

三、容器
在实际的开发过程中,数据结构本身的重要性不会逊于操作于数据结构的算法的重要性,当程序中存在着对时间要求很高的部分时,数据结构的选择就显得更加重要。

经典的数据结构数量有限,但是我们常常重复着一些为了实现向量、链表等结构而编写的代码,这些代码都十分相似,只是为了适应不同数据的变化而在细节上有所出入。STL容器就为我们提供了这样的方便,它允许我们重复利用已有的实现构造自己的特定类型下的数据结构,通过设置一些模版类,STL容器对最常用的数据结构提供了支持,这些模板的参数允许我们指定容器中元素的数据类型,可以将我们许多重复而乏味的工作简化。

容器部分主要由头文件<vector>,<list>,<deque>,<set>,<map>,<stack>和<queue>组成。对于常用的一些容器和容器适配器(可以看作由其它容器实现的容器),可以通过下表总结一下它们和相应头文件的对应关系。

数据结构 描述 实现头文件
向量(vector) 连续存储的元素 <vector>
列表(list) 由节点组成的双向链表,每个结点包含着一个元素 <list>
双队列(deque) 连续存储的指向不同元素的指针所组成的数组 <deque>
集合(set) 由节点组成的红黑树,每个节点都包含着一个元素,节点之间以某种作用于元素对的谓词排列,没有两个不同的元素能够拥有相同的次序 <set>
多重集合(multiset) 允许存在两个次序相等的元素的集合 <set>
栈(stack) 后进先出的值的排列 <stack>
队列(queue) 先进先出的执的排列 <queue>
优先队列(priority_queue) 元素的次序是由作用于所存储的值对上的某种谓词决定的的一种队列 <queue>
映射(map) 由{键,值}对组成的集合,以某种作用于键对上的谓词排列 <map>
多重映射(multimap) 允许键对有相等的次序的映射 <map>

四、迭代器
下面要说的迭代器从作用上来说是最基本的部分,可是理解起来比前两者都要费力一些(至少笔者是这样)。软件设计有一个基本原则,所有的问题都可以通过引进一个间接层来简化,这种简化在STL中就是用迭代器来完成的。概括来说,迭代器在STL中用来将算法和容器联系起来,起着一种黏和剂的作用。几乎STL提供的所有算法都是通过迭代器存取元素序列进行工作的,每一个容器都定义了其本身所专有的迭代器,用以存取容器中的元素。

迭代器部分主要由头文件<utility>,<iterator>和<memory>组成。<utility>是一个很小的头文件,它包括了贯穿使用在STL中的几个模板的声明,<iterator>中提供了迭代器使用的许多方法,而对于<memory>的描述则十分的困难,它以不同寻常的方式为容器中的元素分配存储空间,同时也为某些算法执行期间产生的临时对象提供机制,<memory>中的主要部分是模板类allocator,它负责产生所有容器中的默认分配器。

五、对初学者学习STL的一点建议
对于之前不太了解STL的读者来说,上面的文字只是十分概括地描述了一下STL的框架,对您理解STL的机制乃至使用STL所起到的帮助微乎甚微,这不光是因为深入STL需要对C++的高级应用有比较全面的了解,更因为STL的三个部分算法、容器和迭代器三部分是互相牵制或者说是紧密结合的。从概念上讲最基础的部分是迭代器,可是直接学习迭代器会遇到许多抽象枯燥和繁琐的细节,然而不真正理解迭代器又是无法直接进入另两部分的学习的(至少对剖析源码来说是这样)。可以说,适应STL处理问题的方法是需要花费一定的时间的,但是以此为代价,STL取得了一种十分可贵的独立性,它通过迭代器能在尽可能少地知道某种数据结构的情况下完成对这一结构的运算,所以下决心钻研STL的朋友们千万不要被一时的困难击倒。其实STL运用的模式相对统一,只要适应了它,从一个STL工具到另一个工具,都不会有什么大的变化。

对于STL的使用,也普遍存在着两种观点。第一种认为STL的最大作用在于充当经典的数据结构和算法教材,因为它的源代码涉及了许多具体实现方面的问题。第二种则认为STL的初衷乃是为了简化设计,避免重复劳动,提高编程效率,因此应该是“应用至上”的,对于源代码则不必深究。笔者则认为分析源代码和应用并不矛盾,通过分析源代码也能提高我们对其应用的理解,当然根据具体的目的也可以有不同的侧重。

最后要说的是,STL是ANSI/ISO C++标准的一部分,所以对于一个可以有多种C++实现的过程,首先考虑的应该是STL提供的模板(高效且可移植性好),其次才是各个厂商各自相应的库(高效但可移植性不好)以及自己去编写代码(可移植性好但低效)。
///////////////////////2///////////////////
STL的概念

在STL 中, 大至上分三个主要的功能. 分别是collect class, (例如 vector ,list , .....), 算法部份(如sort ,find ...), 最是一些工具(如auto_ptr , make_pair ....), 其中以collect class 和算法部份最为重要.

STL 使用一种称作 iterator (中文有人翻作泛型指标) 的资料类型来连接collect class 和算法部份. 为了达到能够重复使用算法和collect class. 在STL 中把这些东西更进一步抽象化. 因此STL订义了一堆抽象化的行为称作concept. 您可以想像concept 是在说明一些行为. 只要某一个型别合乎concept 中说明的行为. 就说此类型具有此concept.

举例说明, 如有concept C1,C2. 算法A1, A2. 资料类型D1 具有C1 的行为,D2 有C1,C2.
如A1 要求需要具有C1 的型别. 则任何一个有C1 的资料类型都可以给A1 使用. 在此例中有D1和D2.
如A2 要求具有C1跟C2的型别. 在此只有D2 可以给A2 使用.

STL 使用抽象化的行为定义且实作它们, 来达到算法和资料类型的重复使用.

iterator 在STL 中是很重要的一个概念, iterator 和C 的point 很相, 但他不是指标(note 在某一些状况下iterator 是以C 的point 来实作). 他提供类似point 的功能, 如 '++' 运算表示把iterator 移到下一个element , '*' 运算表示存取iterator 目前指向的element (在此使用'指向'并不是很好). 利用iterator 你可以走访整个collect 中存放的内容.

举个例子说明iteraotr 的功能吧.

vector 是STL 中众多的collect class 中的一种. vector 提供begin() 和end() 这类的member function 来取得vector 中第一个element 的iterator, 和最后一个element的下一个element. 您可以这样使用:

std::vector<int> vtInt;
vtInt.push_back(1);
...
.. // 假设对vtInt 做了很多不同的动做, 如push_back ...

std::vector<int>::iterator itBegin=vtInt.begin();
std::vector<int>::iterator itEnd=vtInt.end();
for( ; itBegin != itEnd ;++itBegin) // 走访itBegin 到 itEnd 不含itEnd 之间的所有element
{
int x=*itBegin; // 取得itBegin 指向的资料

*itBegin = .... ; // 存放资料到itBegin 指向的资料

}

由此例中iterator 有和C point 类似的功能.

机乎所有的STL 的算法都会要求输入的资料是itearot 如sort 算法, 要求输入两个iterator , 然后把这两个iterator 之间的资料加以排序. 介由引进iterator , sort 可以排序的东西就变多了.


使用范例

I. 在STL 中使用标准输出. Say Hello

#include <iostream> // STL 的输入输出包含档. 在STL 中都没有使用.h or .hpp 等副档名

int main(int argc,char* argv[])
{
std::cout << "Hello!! This is STL " << std::endl; // 使用stl 的标准输出, std 是STL 的namespace 所以要这样用std::cout
return 0;
}


II. 从标准输入读入字串, 排序后再输出

#include <iostream> // STL 的输入输出包含档
#include <algorithm> // STL 的算法包含档
#include <string> // STL 的字串
#include <vector> // STL 的一个collect class 这是一个像阵列的class

const int d_nReadLine=5;
int main(int argc,char* argv[])
{

std::vector<std::string> vtString; // 宣告一个字串vector

std::string strTemp; // 宣告一个字串
for( int i = 0 ; i < d_nReadLine ; i++) // 读取d_nReadLine 个字串并将结果存到vtString 中
{
std::cin >> strTemp; // 读入字串
vtString.push_back(strTemp); // 将读入的字串存到vtString 的最后面
}

std::sort(vtString.begin(),vtString.end()); // 将vtString 中得资料sort
std::copy(vtString.begin(),vtString.end(),
std::ostream_iterator<std::string>(std::cout,"/n")); // 将vtString 中得资料输出

return 0;
}

note :

std::sort 和std::copy 都是STL 提供的算法.
///////////////3///////////
//程序:vector演示一
//目的:理解STL中的向量

// #include "stdafx.h" -如果你使用预编译的头文件就包含这个头文件
#include <vector> // STL向量的头文件。这里没有".h"。
#include <iostream> // 包含cout对象的头文件。
using namespace std; //保证在程序中可以使用std命名空间中的成员。

char* szHW = "Hello World";
//这是一个字符数组,以”/0”结束。

int main(int argc, char* argv[])
{
vector <char> vec; //声明一个字符向量vector (STL中的数组)

//为字符数组定义一个游标iterator。
vector <char>::iterator vi;

//初始化字符向量,对整个字符串进行循环,
//用来把数据填放到字符向量中,直到遇到”/0”时结束。
char* cptr = szHW; // 将一个指针指向“Hello World”字符串
while (*cptr != '/0')
{ vec.push_back(*cptr); cptr++; }
// push_back函数将数据放在向量的尾部。

// 将向量中的字符一个个地显示在控制台
for (vi=vec.begin(); vi!=vec.end(); vi++)
// 这是STL循环的规范化的开始——通常是 "!=" , 而不是 "<"
// 因为"<" 在一些容器中没有定义。
// begin()返回向量起始元素的游标(iterator),end()返回向量末尾元素的游标(iterator)。
{ cout << *vi; } // 使用运算符 “*” 将数据从游标指针中提取出来。
cout << endl; // 换行

return 0;
}

/////////////////////4/////////////////////
使用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> 

int 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 

 

--------------------------------------------------------------------------------

使用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";
  }  
};
#
int 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

--------------------------------------------------------------------------------

   使用STL通用算法search在list中找一个序列
一些字符在STL容器中很好处理,让我们看一看一个难处理的字符序列。我们将定义一个list来放字符。 
  list<char> Characters; 

       现在我们有了一个字符序列,它不用任何帮助就知道然后管理内存。它知道它是从哪里开始、到哪里结束。 它非常有用。我不知道我是否说过以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>
#
int 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的整个序列。 

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

 

--------------------------------------------------------------------------------

使用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的教科书。去看看,也许你能发现什么。 

 


本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/washao/archive/2004/12/08/209027.aspx

原创粉丝点击