STL是C++中重要部分之一(面向对象、STL、模板等),其中三个基本的STL组件包括:
1. 迭代器。迭代器之于容器相当于指针之于数组,提供了访问容器对象的方法,事实上C++中的指针也是一种迭代器,但是要注意迭代器不仅仅是指针,不一定具有地址值。
2. 容器。容器是一种模板类,例如list、vector、deque等,一般由迭代器访问容器中的数据。
3. 算法。STL中数据结构和算法是分离的,各种函数在广义容器中(包括链表、数组、string对象、容器)完全通用,只要支持相应的迭代器即可。
1.头文件:
STL头文件一般不使用.h扩展,其中主要使用的头文件和对应容器类如下:
#include Container Class
<deque> deque
<list> list
<map> map, multimap
<queue> queue, priority_queue
<set> set, multiset
<stack> stack
<vector> vector
<string> string
<iterator> ***<***>::iterator
<algorithm> 各种算法函数
STL均使用标准命名空间using namespace std。
2.迭代器:
迭代器有五种类型,这五种类型是一种继承关系,具体如下:
Input iterators:提供对数据的只读访问,前向推进。输入迭代器可以使用==和!=来测试是否相等;使用*来访问数据;使用++操作符前向推进。例如find函数需要保证输入迭代器。
Output iterators:提供对数据的只写访问,前向推进。输出迭代器缺省只写,由于该迭代器无法读取对象,因此不会在任何搜索和其他算法中使用它。
Forward iterators:提供读写访问,前向推进。例如replace函数需要保证前向迭代器。
Bidirectional iterators:提供读写访问,前向或后向推进。例如reverse函数需要保证双向迭代器。
Random access iterators:提供读写访问,随机移动(非const的指针也是随机迭代器)。STL中的排序和搜索函数使用随机访问迭代器,随机访问迭代器可以使用关系操作符做比较。
除此之外,还包括一些特殊的迭代器:
指针迭代器:一个指针也是一种迭代器。
常量迭代器:对于只读变量,为了防止错误赋值,可以使用常量迭代器const_iterator,该迭代器指向的对象不允许改变。注意:const ***<***>::iterator的含义是该迭代器成为常量,不可指向其他数据,与常量迭代器的含义是不一样的。
3.流迭代器
将输入输出(例如标准输入输出流cin/cout或者文件流等)作为容器看待,因此接受迭代器参数的算法都可以和流一起工作。
STL定义模板类ostream_iterator作为输出流迭代器,其构造函数有两个参数,包括一个ostream对象和一个string值(作为间隔符),因此可以象下面一样创建一个迭代器:
ostream_iterator<int> out(cout, “\t”) //定义cout迭代器,可以和任何一个接受输出迭代器的函数一起使用,如copy
copy(....., ......, out);
ofstream out(“text.txt”);
ostream_iterator<string> obegin(out, “\n”); //定义文件流输出迭代器
----------------------------------------
STL定义模板类istream_iterator作为输入流迭代器,可指定读取的来源,并应该和结束迭代器比较。具体如下:
istream_iterator<int> intreader(cin); //定义cin流迭代器,输出结束用ctrl+Z+回车
isteam_iterator<int> eof; //空的流迭代器表示结束
copy(istream_iterator<string>(cin), istream_iterator<string>(), 输出迭代器); //定义无变量名的cin流迭代器
ifstream in(“text.txt”);
istream_iterator<string> ibegin(in);
istream_iterator<string> iend; //定义文件流输入迭代器
还有一些具体应用如下:
//利用流迭代器填充vector
{
ifstream in("test.txt");
istream_iterator<string> ibegin(in);
istream_iterator<string> iend;
vector<string> vec(ibegin, iend); //貌似会有问题,类型不匹配
copy(vec.begin(), vec.end(), ostream_iterator<string>(cout, "\n"));
}
//利用输入流填充vector
{
vector<string> vec;
copy(istream_iterator<string>(cin), istream_iterator<string>(), back_inserter(vec));
sort(vec.begin(), vec.end());
copy(vec.begin(), vec.end(), ostream_iterator<string>(cout,"\n"));
}
//利用流迭代器保存vector内容到文件
{
ifstream in("test.txt");
istream_iterator<string> ibegin(in);
istream_iterator<string> iend;
vector<string> vec(ibegin, iend);
ofstream out("testcopy.txt");
copy(vec.begin(), vec.end(), ostream_iterator<string>(out, "\n"));
}
注意:上面用输入流迭代器来初始化vector后,不可再用这个输入流迭代器,因为随着数据的读取,迭代器已经到达输入流或者文件流的末尾了。
4.插入迭代器:
int arr[] = {1, 2, 3, 4, 5};
vector<int> vi;
copy(arr, arr + 5; vi.begin());
该语句不会执行,因为没有为vi分配存储空间。此时使用插入迭代器可以将值插入到容器中,自动为vi扩展存储空间,主要包括三种插入迭代器。
普通插入器:将对象插入到容器任何对象的前面。该迭代器使用容器的insert操作符替代赋值运算符,第一个参数是容器本身,第二个参数是容器迭代器指定插入位置。
Front inserters:将对象插入到数据集的前面,例如链表表头。该迭代器使用push_front操作替代赋值运算符,参数是容器本身。注意如vector没有push_front的操作,所以不能使用front_inserter迭代器。
Back inserters:将对象插入到数据集的尾部,例如vector的尾部,导致vector容器扩展。该迭代器调用容器的push_back操作替代赋值运算符,参数是容器本身。
注意:使用插入迭代器可能导致容器中的其他对象移动位置,因此现有的迭代器变成非法,需要重新赋值(list除外,不受影响)。
int arr[] = {1, 2, 3, 4, 5};
list<int> vi;
copy(arr, arr + 5; front_inserter(vi));
//最终结果按序是5 4 3 2 1,因为每次调用push_front将一个数据插入到vi的前面。
list<int>::iterator p = find(vi.begin(), vi.end(), 2);
copy(arr, arr + 2, inserter(vi, p));
//最终结果是5 4 3 1 2 2 1,因为调用insert一次性将所有数据插入到p前。
5.混合迭代器函数:
下面两个迭代器函数非常有用:
advance(iterator, int):按照指定的数目增减迭代器,iterator改变。第一个参数是迭代器,第二个参数是增减的数目(前向迭代器该数必须为正,双向或者随机迭代器该数可以为负)。
distance(iterator, iterator):返回到达一个迭代器所需递增操作的数目,即两个迭代器相差的距离。