标准模板库简介

标准模板库STL是一个包含容器类,算法和迭代器的C++库;它提供了很多计算机科学的基本算法和数据结构。STL是一个通用的库,意味着它的组件是非常参数化的:几乎STL中每个组件都是模板。你应该确定你在使用STL之前知道模板在C++中是怎么工作的。

容器和算法

STL就像很多其他类库一样包含容器类:类是用来容纳其他对象的。STL包含了vector,list,deque,set,multiset,map,multimap,hash set,hash multiset,hash map, 和hash multimap这些类。这些类都是模板,它们的实例都可以容纳任何类型的对象。举个例子,你可以使用vector<int>就像使用普通的c数组,除了vector不能手工申请管理动态内存。

vector<int> v(3);              //申明一个3元素的vector

v[0] = 7;

v[1] = v[0] + 3;

v[2] = v[0] + v[1];             // v[0] == 7, v[1] == 10, v[2] == 17

STL也包含了大量的算法用来控制存储在容器中的数据。你可以反转vector中元素的顺序,举个例子,使用reverse算法。

Reverse(v.begin(), v.end());     // v[0] == 17, v[1] == 10, v[2] == 7

这反转函数有重要的两点需要注意。第一,这是一个全局函数而不是成员函数。第二,它有两个参数而不是一个:它作用在一系列的元素上比一个容器要好。在这些特殊的情况下,发生的范围在整个容器v。

对于这两个事实的原因是相同的:reverse,就像STL其他的算法,都是从STL容器类分离出来的。这意味着reverse不仅仅可以用在vectors,还可以用在lists,甚至可以用在C 数组中。下面的程序也是正当的。

double A[6] = {1.2,1.3,1.4,1.5,1.6,1.7};

reverse(A, A + 6);

for(int i=0; i<6; ++i)

cout << “A[“ << i << “]” << A[i];

这个例子利用一个区域,就像那个vector的例子一样:第一个参数给出了指向这个区域的起点,第二个参数指向紧跟着这个区域的末尾的一个元素。这个区域表示[A, A+6];这个不对称的记号提示了这两个端点是不同的,第一个是区域的开始,第二个是紧跟着区域末尾的一个。

迭代器

在C数组逆序的例子中,逆序函数中的参数的类型是double*。要逆序排列vector或者list时,参数是什么呢?逆序函数的参数确切定义是什么呢,还有v.begin(),b.end()到底返回什么呢?

答案是这个逆序函数的参数是迭代器,概括的指针。指针本身是迭代器,这就是为什么C数组也可以使用reverse这个函数。相同的,vector申明了嵌套的iterator和const_iterator。 在上面的例子中,这个类型返回的v.begin()和v.end()是vector<int>::iterator。还有一些迭代器跟容器没有联系,像istream iterator和ostream iterator。

用迭代器这个办法使算法从容器分离开成为可能:算法是模板,用迭代器进行参数化,所以它不受单个容器的约束。考虑,举个例子,怎么写一个算法执行线性搜索一个区域。这个就是STL的find算法。

template<class InputIterator, class T>

InputIterator find(InputIterator first, InputIterator last, const T&value)

{

While( first != last && *first != value) ++first;

Return first;

}

Find需要三个参数:两个迭代器定义了一个区域,还有一个要查找的值。它会查找在这个区域[first,last)内的每个迭代器,从起点到末尾,当找到一个迭代器指向这个值或者到达区域末尾时停止。

First和last 申明输入迭代器,是一个模板参数。也就是说,没有实际的类型InputIterator:当你调用find时,编译器会把他们替换成正式的类型。如果前面两个参数类型是int*,第三个参数类型是int,那么就像调用下面的函数一样。

Int * find(int* first, int* last, const int& value)

{

while(first != last && *first != value) ++first;

return first;

}

概念与建模

一个非常重要的问题是询问模板函数,这些形式上的模板参数会被类型集合正确的取代,而不仅仅是STL算法。非常清楚的,举个例子,int* 或者 double* 用来取代形式上的模板参数InputIterator。同样的,或者不是int 和double:find使用表达式 *first,这个废弃的操作使int对象或者double对象没有意义。这个基本的答案是,find隐含的定义了有这些类型需求的集合,这些实例能够满足这些需求。无论什么类型替代InputIterator都必须提供这些操作:必须可以比较是否相等,必须可以增长,必须可以获得指针等等。

Find不是唯一的需要这一系列要求的STL算法:这些参数用在for each和count,和其他算法时,也必须具备这些条件。这些条件足够重要以至于我们给它取个名字:我们称这些需求为概念,也为这个特殊的概念为Input Interator。一个类型遵循一个概念,或者是一个概念的模型,如果它都满足这些条件。我们称int* 是一个Input Iterator模型,因为int* 提供所有Input Iterator的必须条件。

概念不是C++语言的一部分;不能在一个程序中申明一个概念,或者申明一个特殊的概念模型类型。然而,概念在STL中非常的重要。使用概念使写程序时界面与执行分离成为可能:find的作者只用考虑概念Input Iterator的特殊接口,而不是依照每个可能的类型。同样的,如果你想使用find,你只需要确保这些参数可以通过Input Iterator模型。这就是为什么find和reverse可以使用lists,vectors,C数组,和其他别的类型:概念编程比特殊类型的要好,这样可以使重复使用软件组件和联合组件成为可能。

改进

Input Iterator 实际上是一个很弱的概念:也就是它强迫需要一些需求。一个Input Iterator必须支持一些指针算法(它必须可以通过前缀和后缀的++操作来递增),但是不需要支持所有的指针操作算。这对于find函数已经足够了,但是有些其他的算法需要他们的参数满足更多的需求。举个例子,Reverse,必须能够像递减一样地递增;它使用这个表达式 –last。根据概念而言,reverse的参数必须要是Bidirectional Iterator。

这个Bidirectional Iterator 概念跟Input Iterator 的概念是非常相似的:她仅仅利用一些增加的需求。Bidirectional Iterator模型的类型是Input Iterator模型类型的子集:任何的类型如果是Bidirectional Iterator 那么它也一定是Input Iterator的模型。举个例子,int*,既是Bidrictional Iterator模型又是Input Iterator模型,但是 istream iterator 只是一个Input Iterator模型:它没有遵循更加严格的Bidirectional Iterator 需求。

我们描述Input Iterator 和Bidirectional Iterator之间的关系,Bidirectional Iterator 是一个改进的 Input Iterator。改进这个概念跟C++类的继承非常相似;我们使用不同措辞而不只是把它称为“继承”的原因,强调改进应用于概念比应用于实际的类型要好。

除了我们已经讨论过的两个Iterator外,其实还有3个Iterator的概念:这5个Iterator概念分别是:Output Iterator, Input Iterator, Forward Iterator, Bidirectional Iterator, 和Random Access Iterator;Forward Iterator 是一个改进的Input Iterator, Bidirectional Iterator是一个改进的Forward Iterator,Random Access Iterator 是一个改进的Bidirectional Iterator。(Output Iterator 于其他四个概念有关,但它不是改进层次中的一部分:它不是任何其他Iterator 概念的改进, 没有一个其他的iterator概念是从它改进的。)在Iterator概括中有更详细的信息。

容器类,像迭代器(Iterators),被组织成层次的概念。所有容器都是概念容器的模型;更多精致的概念,就像sequence 和 Associative Container, 在容器类型中有详细描述。

STL的其他部分

如果你了解算法,迭代器,容器,那么你基本上知道STL的一切。但是STL还包含一些其他的组件类型。

首先,STL包含一些utilities。用在库中许多不同部分的非常基本的概念和函数。举个例子,Assignable概念,描述了一个有指派操作和复制构造函数的类型;几乎所有的STL类都是Assignable的模型,几乎所有STL的算法所需要的参数是Assignable模型。

其次,STL包含一些低水平的分配和释放内存的机制。Allocators 是非常专业的,你可以安全的忽视他们因为几乎任何目的。

最后,STL包含大量功能对象,也就是功能函数。正像iterators 是指针的一般化,函数对象是函数的一般化:一个函数对象是你可以使用普通函数调用语法。有一些有关函数对象不一样的概念,包括Unary Function(函数对象只有一个参数,)和Bianry Function(函数对象包含两个参数)。函数对象是一个重要的普通程序的一部分,因为他们允许抽象比类型对象还要多,而且比它正在执行的操作还要多。

原文链接:http://www.sgi.com/tech/stl/stl_introduction.html

posted on 2010-08-07 17:21 轻轻听 阅读(...) 评论(...) 编辑 收藏

导航

统计