c++ STL

一个好网站:

http://stlchina.huhoo.net/twiki/bin/view.pl/Main/WebHome

 

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

 

STL入门教程 

http://stlchina.huhoo.net/twiki/bin/view.pl/Main/STLIntroduce

 

标准模板库(STL)介绍

作者: Scott Field
来源:最优秀的STL学习网站

本文以List容器为例子,介绍了STL的基本内容,从容器到迭代器,再到普通函数,而且例子丰富,通俗易懂。不失为STL的入门文章,新手不容错过!

 

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() 函数来得到过去的最后一个值(就是处理停在那的那个值)。

这就是STL所有的东西,容器、算法、和允许算法工作在容器中的元素上的iterator。 算法以合适、标准的方法操作对象,并可通过iterator得到容器精确的长度。一旦做了这些,它们就在也不会“跑出边界”。 还有一些其他的对这些核心组件类型有功能性增强的组件,例如函数对象。我们将会看到有关这些的例子,现在 ,我们先来看一看STL的list。

 

1 定义一个list


我们可以象这样来定义一个STL的list:

#include <string>
#include <list>
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用来放字符串。

 

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


#include <string>
#include <list>

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 <string>
#include <list>
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;
}

 

4 用for循环来处理list中的元素

我们想要遍历一个list,比如打印一个中的所有对象来看看list上不同操作的结果。要一个元素一个元素的遍历一个list, 我们可以这样做:

/*
|| How to print the contents of a simple STL list. Whew! 
*/
#include <iostream.h>
#include <string>
#include <list>

int 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 <string>
#include <list>
#include <algorithm>

PrintIt (string& StringToPrint) {
  cout << StringToPrint << endl;
}

int 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);
}

在这个程序中我们使用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>
#
int 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);
#
  cout << "There were " << NumberOf100Scores << " scores of 100" << endl;
}

count()算法统计等于某个值的对象的个数。上面的例子它检查list中的每个整型对象是不是100。每次容器中的对象等于100,它就给NumberOf100Scores加1。这是程序的输出:

There were 2 scores of 100

 

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>

const string ToothbrushCode("0003");

class IsAToothbrush 
{
public:  
   bool operator() ( string& SalesRecord ) 
   {
     return SalesRecord.substr(0,4)==ToothbrushCode;
   }     
};

int 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);  
  count_if (SalesRecords.begin(), SalesRecords.end(), 
             IsAToothbrush(), NumberOfToothbrushes);

  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()将对该容器的每个对象调用这个函数。

 

7 使用count_if()的一个更加复杂的函数对象。


我们可以更进一步的研究一下函数对象。假设我们需要传递更多的信息给一个函数对象。我们不能通过 调用operator来作到这点,因为必须定义为一个list的中的对象的类型。 然而我们通过为IsAToothbrush指出一个非缺省的构造函数就可以用任何我们所需要的信息来初始化它了。 例如,我们可能需要每个牙刷有一个不定的代码。我们可以把这个信息加到下面的函数对象中:

 

 
/*
|| Using a more complex function object
*/
#include <iostream.h>
#include <string>
#include <list>
#include <algorithm>

class IsAToothbrush 
{
public:
  IsAToothbrush(string& InToothbrushCode) : 
      ToothbrushCode(InToothbrushCode) {}
  bool operator() (string& SalesRecord) 
  {
    return SalesRecord.substr(0,4)==ToothbrushCode;
  }       
private:
  string ToothbrushCode;        
};

int 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);  
  count_if (SalesRecords.begin(), SalesRecords.end(), 
              IsAToothbrush(VariableToothbrushCode),
                 NumberOfToothbrushes);
  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中的对象记数
  • 如何定义一个函数对象

我选用这些例子来演示list的一般操作。如果你懂了这些基本原理,你就可以毫无疑问的使用STL了 建议你作一些练习。我们现在用一些更加复杂的操作来扩展我们的知识,包括list成员函数和STL通用算法。

 

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>

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

 

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";
    }
};
 
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

 

10 使用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()的值。

 

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>
 
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

 

12 用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指出的位置以前。

 

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>
 
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

 

15 用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

 

16 使用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);
}

输出结果将为:
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函数来删除从新结尾到老结尾的部分。

 

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>
 
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

 

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写过几个商业程序。它在很多方面减少了我的工作量,也排除了很多逻辑错误。

  最大的一个程序有大约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 参考书目:

  1. The STL Tutorial and Reference Guide, David Musser and Atul Saini. Addison Wesley 1996. 《STL教程和参考手册》
  2. The C++ Programming Language 3e, Bjarne Stroustrup. Addison Wesley 1997

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

http://www.cnblogs.com/shiyangxt/archive/2008/09/11/1289493.html

 

学无止境!!!

第一部分:(参考百度百科)

 一、STL简介

STLStandard Template Library,标准模板库)是惠普实验室开发的一系列软件的统称。它是由Alexander StepanovMeng LeeDavid R Musser在惠普实验室工作时所开发出来

的。现在虽说它主要出现在C++中,但在被引入C++之前该技术就已经存在了很长的一段时间。

 

STL的代码从广义上讲分为三类:algorithm(算法)、container(容器)和iterator(迭代器),几乎所有的代码都采用了模板类和模版函数的方式,这相比于传统的由函数和类

 

组成的库来说提供了更好的代码重用机会。在C++标准中,STL被组织为下面的13个头文件<algorithm><deque><functional><iterator><vector>、<list>、<map>、

<memory><numeric><queue><set><stack><utility> 

二、算法

大家都能取得的一个共识是函数库对数据类型的选择对其可重用性起着至关重要的作用。举例来说,一个求方根的函数,在使用浮点数作为其参数类型的情况下的可重用性肯定比

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

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

第二部分:

暂且举几个非常容易理解的程序源码:

 

复制代码
#include  < iostream >
#include 
< vector >
using   namespace  std;
int  main(){
    vector
< int > vi;
    
int  a;
    
while ( true )
    {
    cout
<< " 输入一个整数,按0停止输入: " ;
    cin
>> a;
    
if (a == 0 )
    
break ;
    vi.push_back(a);   
    vector
< int > ::iterator iter;
    
for (iter = vi.begin();iter != vi.end(); ++ iter)
    cout
<<* iter;      
    }
    
return   0 ;
    }
复制代码

 

 

复制代码
#include  < iostream >  
#include 
< string >
#include 
< vector >  
using   namespace  std; 
int  main(){
string  str = " shiyang "
vector 
< string >  vecstr; 
vecstr.push_back(str);
vector 
< string >  ::iterator iter =  vecstr.begin(); 
cout
<<* iter << endl;
return   0 ;
}
复制代码

 

 

复制代码
#include    < stdlib.h >    
#include   
< windows.h >    
#include   
< conio.h >    
#include   
< map >     // STL
#include    < functional >   // STL  
#include    < algorithm >     // STL
#include    < iostream >    
using   namespace  std;   
typedef  map
< int , int *>  m_iip;   
typedef  map
< int , char *>  m_icp;   
class   f_c{   
  
int  _i;   
  
public :   
  f_c(
int  i):_i(i){
          
  }   
  
void   operator ()(m_iip::value_type ite)   
  {   
  cout
<< _i ++<< " \t " << ite.first << "  shi " << endl;   
  }   
  
void   operator ()(m_icp::value_type ite)  
  {   
  cout
<< _i ++<< " \t " << ite.first << "  yang " << endl;  
  }   
  };   
  
void  f( int  i, int  c)   
  {   
      
  }   
  
int  main( int  argc, char *  argv[]){
  m_iip  iip;   
  m_icp  icp;   
  
int  i = 0 ;   
  iip.insert(make_pair(
34 , & i));   
  iip.insert(make_pair(
67 , & i));   
  iip.insert(make_pair(
5 , & i));   
  iip.insert(make_pair(
342 , & i));    
  
char  d = 0 ;   
  icp.insert(make_pair(
12 , & d));   
  icp.insert(make_pair(
54 , & d));   
  icp.insert(make_pair(
6 , & d));   
  icp.insert(make_pair(
92 , & d));   
  for_each(iip.begin(),iip.end(),f_c(
8 ));   
  for_each(icp.begin(),icp.end(),f_c(
65 )); //    
   return   0 ;   
  }
复制代码

 

 

复制代码
#include  < iostream >
#include 
< list >
#include 
< numeric >
#include 
< algorithm >
using   namespace  std;
// 创建一个list容器的实例LISTINT
typedef list < int >  LISTINT;

// 创建一个list容器的实例LISTCHAR
typedef list < int >  LISTCHAR;

int  main(){
    
// --------------------------
    
// 用list容器处理整型数据
    
// --------------------------
    
// 用LISTINT创建一个名为listOne的list对象
    LISTINT listOne;
    
// 声明i为迭代器
    LISTINT::iterator i;

    
// 从前面向listOne容器中添加数据
    listOne.push_front ( 2 );
    listOne.push_front (
1 );

    
// 从后面向listOne容器中添加数据
    listOne.push_back ( 3 );
    listOne.push_back (
4 );

    
// 从前向后显示listOne中的数据
    cout << " listOne.begin()--- listOne.end(): " << endl;
    
for  (i  =  listOne.begin(); i  !=  listOne.end();  ++ i)
        cout 
<<   * <<   "   " ;
    cout 
<<  endl;

    
// 从后向前显示listOne中的数据
LISTINT::reverse_iterator ir;
    cout
<< " listOne.rbegin()---listOne.rend(): " << endl;
    
for  (ir  = listOne.rbegin(); ir != listOne.rend();ir ++ ) {
        cout 
<<   * ir  <<   "   " ;
    }
    cout 
<<  endl;

    
// 使用STL的accumulate(累加)算法
     int  result  =  accumulate(listOne.begin(), listOne.end(), 0 );
    cout
<< " Sum= " << result << endl;
    cout
<< " ------------------ " << endl;

    
// --------------------------
    
// 用list容器处理字符型数据
    
// --------------------------

    
// 用LISTCHAR创建一个名为listOne的list对象
    LISTCHAR listTwo;
    
// 声明i为迭代器
    LISTCHAR::iterator j;

    
// 从前面向listTwo容器中添加数据
    listTwo.push_front ( ' A ' );
    listTwo.push_front (
' B ' );

    
// 从后面向listTwo容器中添加数据
    listTwo.push_back ( ' x ' );
    listTwo.push_back (
' y ' );

    
// 从前向后显示listTwo中的数据
    cout << " listTwo.begin()---listTwo.end(): " << endl;
    
for  (j  =  listTwo.begin(); j  !=  listTwo.end();  ++ j)
        cout 
<<   char ( * j)  <<   "   " ;
    cout 
<<  endl;

    
// 使用STL的max_element算法求listTwo中的最大元素并显示
    j = max_element(listTwo.begin(),listTwo.end());
    cout 
<<   " The maximum element in listTwo is:  " << char ( * j) << endl;
}
复制代码

 

 

复制代码
#include  < iostream >
#include 
< list >

using   namespace  std;
typedef list
< int >  INTLIST;

// 从前向后显示list队列的全部元素
void  put_list(INTLIST list,  char   * name)
{
    INTLIST::iterator plist;

    cout 
<<   " The contents of  "   <<  name  <<   "  :  " ;
    
for (plist  =  list.begin(); plist  !=  list.end(); plist ++ )
        cout 
<<   * plist  <<   "   " ;
    cout
<< endl;
}

// 测试list容器的功能
int  main(){
// list1对象初始为空
    INTLIST list1;   
    
// list2对象最初有10个值为6的元素 
    INTLIST list2( 10 , 6 ); 
    
// list3对象最初有9个值为6的元素 
    INTLIST list3(list2.begin(), -- list2.end()); 

    
// 声明一个名为i的双向迭代器
    INTLIST::iterator i;

    
// 从前向后显示各list对象的元素
    put_list(list1, " list1 " );
    put_list(list2,
" list2 " );
    put_list(list3,
" list3 " );
    
// 从list1序列后面添加两个元素
list1.push_back( 2 );
list1.push_back(
4 );
cout
<< " list1.push_back(2) and list1.push_back(4): " << endl;
    put_list(list1,
" list1 " );

// 从list1序列前面添加两个元素
list1.push_front( 5 );
list1.push_front(
7 );
cout
<< " list1.push_front(5) and list1.push_front(7): " << endl;
    put_list(list1,
" list1 " );

// 在list1序列中间插入数据3个9 
list1.insert( ++ list1.begin(), 3 , 9 );
cout
<< " list1.insert(list1.begin(),3,9): " << endl;
    put_list(list1,
" list1 " );

// 测试引用类函数
cout << " list1.front()= " << list1.front() << endl;
cout
<< " list1.back()= " << list1.back() << endl;

// 从list1序列的前后各移去一个元素
list1.pop_front();
list1.pop_back();
cout
<< " list1.pop_front() and list1.pop_back(): " << endl;
    put_list(list1,
" list1 " );

// 清除list1中的第2个元素
list1.erase( ++ list1.begin());
cout
<< " list1.erase(++list1.begin()): " << endl;
    put_list(list1,
" list1 " );

// 对list2赋值并显示
list2.assign( 8 , 1 );
cout
<< " list2.assign(8,1): " << endl;
    put_list(list2,
" list2 " );

// 显示序列的状态信息
cout << " list1.max_size():  " << list1.max_size() << endl;
cout
<< " list1.size():  " << list1.size() << endl;
cout
<< " list1.empty():  " << list1.empty() << endl;

// list序列容器的运算
    put_list(list1, " list1 " );
    put_list(list3,
" list3 " );
cout
<< " list1>list3:  " << (list1 > list3) << endl;
cout
<< " list1<list3:  " << (list1 < list3) << endl;

// 对list1容器排序
list1.sort();
    put_list(list1,
" list1 " );
    
// 结合处理
list1.splice( ++ list1.begin(), list3);
    put_list(list1,
" list1 " );
    put_list(list3,
" list3 " ); 
}
复制代码

 

第三部分:

50条忠告:(其中有几条觉得写的不够贴切,所以删了,发了余下的部分)

 

1.把C++当成一门新的语言学习;

2.看《Thinking In C++》,不要看《C++变成死相》;

3.看《The C++ Programming Language》和《Inside The C++ Object Model》,不要因为他们很难而我们自己是初学者所以就不看;

4.不要被VC、BCB、BC、MC、TC等词汇所迷惑——他们都是集成开发环境,而我们要学的是一门语言;

5.不要放过任何一个看上去很简单的小编程问题——他们往往并不那么简单,或者可以引伸出很多知识点;

6.会用Visual C++,并不说明你会C++;

7.学class并不难,template、STL、generic programming也不过如此——难的是长期坚持实践和不遗余力的博览群书;

8.如果不是天才的话,想学编程就不要想玩游戏——你以为你做到了,其实你的C++水平并没有和你通关的能力一起变高——其实可以时刻记住:学C++是为了编游戏的;

9.看Visual C++的书,是学不了C++语言的;  

16.把时髦的技术挂在嘴边,还不如把过时的技术记在心里;

18.学习编程最好的方法之一就是阅读源代码;

19.在任何时刻都不要认为自己手中的书已经足够了;

20.请阅读《The Standard C++ Bible》(中文版:标准C++宝典),掌握C++标准;

21.看得懂的书,请仔细看;看不懂的书,请硬着头皮看;

22.别指望看第一遍书就能记住和掌握什么——请看第二遍、第三遍;

23.请看《Effective C++》和《More Effective C++》以及《Exceptional C++》;

24.不要停留在集成开发环境的摇篮上,要学会控制集成开发环境,还要学会用命令行方式处理程序;

25.和别人一起讨论有意义的C++知识点,而不是争吵XX行不行或者YY与ZZ哪个好;

26.请看《程序设计实践》,并严格的按照其要求去做;

27.不要因为C和C++中有一些语法和关键字看上去相同,就认为它们的意义和作用完全一样;

28.C++绝不是所谓的C的“扩充”——如果C++一开始就起名叫Z语言,你一定不会把C和Z语言联系得那么紧密;

29.请不要认为学过XX语言再改学C++会有什么问题——你只不过又在学一门全新的语言而已;

30.读完了《Inside The C++ Object Model》以后再来认定自己是不是已经学会了C++;

31.学习编程的秘诀是:编程,编程,再编程;

32.请留意下列书籍:《C++面向对象高效编程(C++ Effective Object-Oriented Software Construction)》《面向对象软件构造(Object-Oriented Software Construction)》《设计模式(Design Patterns)》《The Art of Computer Programming》; 

34.请把书上的程序例子亲手输入到电脑上实践,即使配套光盘中有源代码;

35.把在书中看到的有意义的例子扩充;

36.请重视C++中的异常处理技术,并将其切实的运用到自己的程序中;

37.经常回顾自己以前写过的程序,并尝试重写,把自己学到的新知识运用进去;

38.不要漏掉书中任何一个练习题——请全部做完并记录下解题思路;

39.C++语言和C++的集成开发环境要同时学习和掌握;

40.既然决定了学C++,就请坚持学下去,因为学习程序设计语言的目的是掌握程序设计技术,而程序设计技术是跨语言的;

41.就让C++语言的各种平台和开发环境去激烈的竞争吧,我们要以学习C++语言本身为主;

42.当你写C++程序写到一半却发现自己用的方法很拙劣时,请不要马上停手;请尽快将余下的部分粗略的完成以保证这个设计的完整性,然后分析自己的错误并重新设计和编写(参见43);

43.别心急,设计C++的class确实不容易;自己程序中的class和自己的class设计水平是在不断的编程实践中完善和发展的;

44.决不要因为程序“很小”就不遵循某些你不熟练的规则——好习惯是培养出来的,而不是一次记住的;

45.每学到一个C++难点的时候,尝试着对别人讲解这个知识点并让他理解——你能讲清楚才说明你真的理解了;

46.记录下在和别人交流时发现的自己忽视或不理解的知识点;

47.请不断的对自己写的程序提出更高的要求,哪怕你的程序版本号会变成Version 100.XX;

48.保存好你写过的所有的程序——那是你最好的积累之一;

49.请不要做浮躁的人;

50.请热爱C++!

第四部分:

C++头文件一览 
C、传统 C++

#include <assert.h>    设定插入点
#include <ctype.h>    字符处理
#include <errno.h>     定义错误码
#include <float.h>    浮点数处理
#include <fstream.h>   文件输入/输出
#include <iomanip.h>    参数化输入/输出
#include <iostream.h>   数据流输入/输出
#include <limits.h>    定义各种数据类型最值常量
#include <locale.h>    定义本地化函数
#include <math.h>     定义数学函数
#include <stdio.h>    定义输入/输出函数
#include <stdlib.h>    定义杂项函数及内存分配函数
#include <string.h>    字符串处理
#include <strstrea.h>   基于数组的输入/输出
#include <time.h>     定义关于时间的函数
#include <wchar.h>     宽字符处理及输入/输出
#include <wctype.h>    宽字符分类

标准 C++ 

#include <algorithm>     通用算法
#include <bitset>      位集容器
#include <cctype>
#include <cerrno>
#include <clocale>
#include <cmath>
#include <complex>     复数类
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <deque>      双端队列容器
#include <exception>    异常处理类
#include <fstream>
#include <functional>    定义运算函数(代替运算符)
#include <limits>
#include <list>       线性列表容器
#include <map>       映射容器
#include <iomanip>
#include <ios>      基本输入/输出支持
#include <iosfwd>    输入/输出系统使用的前置声明
#include <iostream>
#include <istream>     基本输入流
#include <ostream>     基本输出流
#include <queue>       队列容器
#include <set>       集合容器
#include <sstream>     基于字符串的流
#include <stack>      堆栈容器    
#include <stdexcept>    标准异常类
#include <streambuf>   底层输入/输出支持
#include <string>     字符串类
#include <utility>     通用模板类
#include <vector>     动态数组容器
#include <cwchar>
#include <cwctype>

C99 增加

#include <complex.h>  复数处理
#include <fenv.h>    浮点环境
#include <inttypes.h>  整数格式转换
#include <stdbool.h>   布尔环境
#include <stdint.h>   整型环境
#include <tgmath.h>  通用类型数学宏

你可能感兴趣的:(C++)