尺度模板库先容
副标题#e#
尺度模板库,也叫 STL,是一个 C++ 容器类库,算法和迭代器。他提供很多 根基算法,数据布局。STL 是一个通用库,即可以充份定制:险些所有的 STL 组件都是模板。在你利用 STL 前,你必需相识模板的事情环境。
容器和算法
和很多类库一样,STL 包括容器类 – 可以包括其他工具的类。STL 包括向量 类,链表类,双向行列类,荟萃类,图类,等等。他们中的每个类都是模板,能包括 各类范例的工具。譬喻,你可以用 vector<int> ,就象通例的 C 语 言中的数组,除了 vector 不要你象数组那样思量到动态内存分派的问题。
vector<int> v(3); // 界说一个有三个元素的向量类
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
在挪用 reverse 的时候有两点要留意。首先,他是个全局函数,而不是成员 函数。其次,他有两个参数,而不是一个:他操纵必然范畴的元素而不是操纵容器。 在这个例子中他正好是对整个容器 V 操纵。
以上两点的原因是沟通的:reverse 和其他 STL 算法一样,他们是通用的,也就 是说, reverse 不只可以用来颠倒向量的元素,也可以颠倒链表中元素的顺序。 甚至可以对数组操纵。下面的措施是正当的。
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];
这个例子也用到了范畴,和我们上面的向量的例子一样:第一个参数是指向要操纵的 范畴的头的指针,第二个参数是指向尾的指针。在这里,范畴是 [A, A + 6)。大概 你留意到范畴的差池称。
迭代器
在上面的 C 数组的例子中,reverse 的参数范例是 double*。那么在 向量 或链表中参数又是什么呢?也就是说,毕竟 reverse 是 如何界说参数的,而且 v.begin() 和 v.end() 返回什么?
问题的谜底是 reverse 的参数是 迭代器 (iterators)。他是指针的归纳综合。 指针本身也是迭代器。这也是为什么可以颠倒数组中元素的顺序的原因。同样地,向量 界说了嵌套的范例 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 范例, 而 InputIterator 是个模板参数。也就是说,实际上没有一个真实的范例 InputIterator:当你挪用 find 时,编译器用实际的范例 去匹配 InputIterator 和 T。假如 find 的两个参数 中第一个实现是 int*,第三个实现是 int,那么你可以认为是在执行 下面的函数。
int* find(int* first, int* last, const int& value) {
while (first != last && *first != value) ++first;
return first;
}
#p#副标题#e#
约束和类属实参
对付函数模板 (不只仅是 STL 算法) 来说,一个很是重要的问题是:用什么样的参数 范例去匹配模板参数才正确?为具体说明这个问题,我们举例:显然,int* 或 double* 可以或许取代 find 的模板参数 InputIterator。而 int 或 double 则不可。因此,有个显然的谜底是:find 隐含地界说了对付范例的要求。无论用什么范例来实现 InputIterator,都必需 提供必然的操纵:他必需可以或许较量两个工具的巨细,他必需可以或许举办增加操纵。
Find 不是仅有的一个有如此要求的 STL 算法。for_each 和 count 以及其他算法,都要满意上面的要求。这些要求很重要,所以我们给他取个名字:我们将这一 类范例条件叫约束 (concept)。我们称上面的约束为 Input Iterator。假如 一个范例满意所有的要求,那么我们说这个范例切合该约束,可能说他是该约束的类属实参 (Model)。我们说 int* 是 Input Iterator 的类属实参,因为 int* 能提供 Input Iterator 所要求的所有操纵。
#p#分页标题#e#
约束可不是 C++ 语言的一部份,在一个措施中你没有步伐去界说一个约束,可能界说某范例是约束的类属实参。然而,约束是 STL 中的很重要的一部份。利用约束类属机制让我们在措施中从措施实现中疏散接口成为大概。find 的作者只要思量接口只针对付 Input Iterator 而不是去实现切合该约束的所有大概数据范例。同样,假如你要用 find,那么你只要确定你通报的参数是 Input Iterator 的类属实参就可以了。这就是为什么 find 和 reverse 可以或许在 list, vector, C 数组合其他范例中利用。用约束(的思想)去编程,而不是牢靠于某一特定范例,让措施重用组件和将组件组合利用。
Refinement
Input Iterator 实际上是相当弱的约束。也就是说,他只有少数几个要求。一个 Input Iterator 必需支持指针运算的子集(他必需可以或许通过操纵符 operator++ 来增加 Input Iterator ),可是不需要其支持所有的指针算法。这对付 find 来说足够了,可是其他的一些算法就不是这样的,他们需要满意别的的条件。譬喻 Reverse 还要可以或许支持操纵符 –。用术语来说,就是 reverse 的参数要是 Bidirectional Iterator 而非 Input Iterator。
Bidirectional Iterator 的约束和 Input Iterator 的约束很临近:他只是简朴地增加了一点别的的条件。Bidirectional Iterator 的类属实参是 Input terator 类属实参的子集:每个是 Bidirectional Iterator 类属实参的数据范例都是 Input Iterator 的类属实参。譬喻 Int* 等于 Bidirectional Iterator 的类属实参,也是 Input Iterator 的类属实参。可是 istream_iterator就差异了,他只是 Input Iterator 的类属实参:他不能“适应”更严格的 Bidirectional Iterator 的要求。
我们这样来描写 Input Iterator 和 Bidirectional Iterator 的干系: Bidirectional Iterator 是 Input Iterator 的 refinement。约束的 refinement 很是象 C++ 类的担任。我们用差异的词的主要原因是 refinement 用于约束而不是实际的范例。
除了我们上面提到的两个迭代器约束外,尚有三个迭代器约束。这五个迭代器 约束是: Output Iterator, Input Iterator, Forward Iterator, Bidirectional Iterator 和 Random Access Iterator。Forward Iterator 是 Input Iterator 的 refinement,Bidirectional Iterator 又是 Forward Iterator 的 refinement, Random Access Iterator 是 Bidirectional Iterator 的 refinement。(Output Iterator 和其他四个约束有干系,可是不是 refinement 条理上的干系:他不是其他四个的 refinement,其他四个也不是他的 refinement。)
容器类和迭代器一样,也有约束的条理干系。所有的容器是 Container 的类属实参。譬喻 Sequence 和 Associative Container,都是详细的容器。
STL 的其他部份
假如你领略了算法,迭代器,容器,那么你险些就相识了 STL。可是 STL 还包括其他几个部份的内容。
首先,STL 包括几个 “东西”:很是根基的在类库中差异处所呈现的函数和 concept。譬喻,Assignable 约束, 就描写了那些可以赋值和类的拷贝操纵的范例。险些所有的 STL 类都是 Assignable 的类属实参。险些所有的算法都要求其参数是 Assignable 的类属实参。
其次,STL 包罗了底层的内存的分派和释放。Allocators 很是少见,你险些可以忽略他们。
最后,STL 包括了大量的 工具函数,也称为 functors。就象迭代器是指针的一般形式一样,工具函数是函数的一般形式。工具函数有几种差异的约束,包罗 Unary Function (一种只有一个参数的工具函数,如 f(x)) 和 Binary Function (一种有两个参数的工具函数,如 f(x, y))。工具函数对付编程来说很重要,因为他如同工具范例的抽象一样浸染于操纵。