STL(Standard Template LIbrary)标准模板库提供了大量的模板类和模板函数,大大简化了数据结构和算法的代码编写任务。
考虑到在编译和应用上的一些因素,如模板类型的安全性检查、编译速度和减少语言的依赖性等,C++STL在广泛使用模板编程的同时,还引入了诸多concept概念、迭代器和函数对象等技术性的内容。
一、C++ STL的发展历程
1971年左右引进泛型设计
1984年Alexandar Sepanov与Musser合作,分别在Scheme语言和Ada语言上开发出泛型的数据结构和算法函数库。
1987年,Alexandar Sepanov转而为C/C++语言开发泛型库,当时C++还未提供模板的编程技术。一同在AT&T实验室工作的C++创始人Bjarne Stroustrup,从Ada语言中借鉴泛型编程,为C++语言引入了模板的编程,以简化C/C++泛型库的开发。
1988年 Alexandar Sepanov 进入HP工作
1992年 Alexandar Sepanov重新返回到C++STL泛型库的研究,最终与Meng Lee合作实现了HP版本的C++ STL
1993年Alexandar Sepanov向ANSI/ISO C++标准委员会建议,将STL通用库纳入C++标准
1998年 ,STL成为标准化
为了确切把握STL泛型库所蕴含的设计思想,这里介绍一下STL之父Alexandar Sepanov的一些背景
Alexandar Sepanov,1950年出生于前苏联的莫斯科。曾在莫斯科大学研究数学。他在研究泛型库时,首先是从算法着手,找到具体算法步骤之后,就分析算法每一步骤所需要的条件,然后再归并出整个算法所需要满足的最一般性条件,即最弱的条件。所找出的这些算法条件,其实就是C++ STL所谓的concept概念。
如果将泛型库的泛化工作与20世纪中叶由法国布尔巴基学派所倡议的结构数学的研究作一比较,会发现两者殊途同归,都是从具体的证明步骤或算法步骤中,提炼出公理性条件,然后再从这个公理性条件出发,参考具体情形下的证明或算法步骤,给出一般性的证明步骤或算法步骤。STL的迭代器Iterator是C/C++指针的一般化推广。
二、STL的体系结构
STL泛型库由容器、迭代器、算法、函数对象、适配器、内存分配器、概念以及模型等几大部分组成,其中最关键的组件是容器和算法,其他则是围绕它们进行开发或使用的附带产物。
1、容器(Container)
泛型数据结构用模板类给出,统称为容器类,包括vector, list, queue, set, 等,每个容器类都可以传入一个具体的类型,具现出一个非模板的类。
2、迭代器(Iterator)
传统上读写数据结构中的数据,一般都是通过移动读写指针来进行的。在STL中,对容器的数据读写,通过迭代器来进行。迭代器是指针的一个泛化,在容器和算法之间充当桥梁的作用,是STL泛型库最核心的一个组成部分。
具体地说,各种C++ STL容器都需要定义一个迭代器,以指示容器的当前数据读写位置,有两种比较常用的方法
一是简单地定义,在容器类X中定义一个迭代器iterator,如下:
template<class T>
class X {
public:
typedef T* iterator; //定义迭代器iterator
...
};
另一种则是采用一个类或结构体定义出来,如链表节点指针泛化的迭代器,需要在迭代器中重载“++”或“--”操作符。
3、算法(Algorithm)
把常用的排序、交换、查找和搜索等算法以泛化算法实现到C++ STL库中,避免不必要的重复设计。
如果将算法作为各个容器的成员函数,放在容器中进行实现,算法将依赖于具体的数据结构,明显会有失应用的一般性。
*把数据结构中用来读写数据的指针,泛化为迭代器,然后,将算法架构在迭代器之上,把迭代器值作为它的参数,就可脱离具体的容器而实现出通用的算法函数 ,如下面find算法函数表示,参数first和last是迭代器,指示查找的起始和结束位置,val参数是所要查找的某个类型值。
template <class InputIter, class T>
inline InputIter find(InputIter first, InputIter last, const T& val, input_iterator_tag)
{
while (first != last && !(*first == val))
++first;
return first;
}
*把算法应用到容器,只需将相关的迭代器值传入算法即可。下面是一个用find算法在vector上查找某值元素的例子,返回所找到元素的迭代器值。
#include <vector>
#include <iostream>
#include <algorithm>
int main(void)
{
using namespace std;
vector<int> v;
v.push_back(6);
v.push_back(8);
v.push_back(31);
//
vector<int>::iterator result = find(v.begin(), v.end(), 8);
cout << *result << endl;
return 0;
}
4、函数对象(Function Object)
函数的传递当然可用泛化的函数指针来进行,但是STL常使用的是函数对象,目的在于用更简洁、不依赖于当前计算机硬件体系的方式来表达算法。
template <class InputIter, class Function>
Function for_each(InputIter first, InputIter last, Function f) {
for (; first != last; ++first)
f(*first); //调用函数
return f; //返回函数对象
}
STL中,定义了若干函数对象,包括算术运算plus, minus, 等
5、适配器(Adapter)
STL中所渭的适配器,作用相当于一个类型转换。
6、内存分配器(Allocator)
STL的内存分配器是一些用于内存管理的模板类,可支持高层次和高性能的内存管理。内存分配器通过malloc和realloc函数进行底层的内存分配,一次分配大块的内存后,动态生成的对象内存分配,可在大块内存中依最小碎片原则进行划分和重整。内存分配器为若干对象的内存分配和释放提供了高级形式的调用,方便各种容器对元素数据进行内存管理。
7、概念(Concept)和模型(Model)
在代码的开发阶段及时发现错误,一个较为有效的方法是,为泛型库算法函数的所有模板类型,从算法的实现步骤中归纳出相应的一个概念,用概念来指出用于具现的类型必须满足的运算条件,而满足某个概念所定义条件的类型,则称为该概念的一个模型。
例如,InputIterator概念指出了迭代器必须具有!=, ==, *和++运算,Int* 和 double* 指针都是InputIterator概念的一个模型。
五、STL存在的一些问题