理解迭代器是理解STL的关键所在。模板使得算法独立于存储的数据类型,而迭代器使算法独立于使用的容器类型。因此,它们都是STL通用方法的重要组成部分。
迭代器类型
1. 输入迭代器
输入迭代器可被程序用来读取容器中的信息,但不一定能让程序修改值。
输入迭代器是单向迭代器,可以递增,但不能倒退。
对于单通行(single-pass)、只读算法,可以使用输入迭代器。
2. 输出迭代器
对于单通行、只写算法,可以使用输入迭代器。
3. 正向迭代器
正向迭代器只使用++操作符来遍历容器,它每次沿容器向前移动一个元素。它总是按相同的顺序遍历一系列值。另外,将正向迭代器递增后,仍然可以对前面的迭代器值解除引用(如果保存了它),并可以得到相同的值。这些特征使得多次通行算法成为可能。
4. 双向迭代器
双向迭代器具有正向迭代器的所有特征,同时支持两种(前缀和后缀)递减操作符。
5. 随机访问迭代器
随机访问迭代器具有双向迭代器的所有特征,同时添加了支持随机访问的操作(如指针增加运算)和用于对元素进行排序的关系操作符。
各种迭代器的类型并不是确定的,而只是一种概念性描述。
每个容器类都定义了一个类级typedef名称——iterator
下图总结了主要的迭代器功能。
预定义迭代器
ostream_iterator迭代器
该模板是输出迭代器概念的一个模型,它也是一个适配器(adapter)——一个类或函数,可以将一些其他接口转换为STL使用的接口。
可以通过包含头文件iterator(以前为iterator.h)并做下面的声明来创建这种迭代器:
#include <iterator>
...
ostream_iterator<int,char> out_iter(cout," ");
out_iter迭代器现在是一个接口,让其能够使用cout来显示信息。
第一个模板参数(这里是int)指出了被发送给输出流的数据类型;第二个模板参数(这里是char)指出了输出流使用的字符类型(另一个可能的值是wchar_t)。
构造函数的第一个参数(这里是cout)指出了要使用的输出流,它也可以用于文件输出的流;最后一个字符串参数是在发送给输出流的每个数据项后显示的分隔符。
在老式C++实现中,只使用了ostream_iterator的第一个模板参数。
使用out_iter迭代器:
*out_iter++ = 15; // works like cout << 15 << " ";
上述代码将15和由空格组成的字符串发送到cout管理的输入流中,并为下一个输出操作做好了准备。
还可以将该迭代器用于算法中,这里使用copy()算法:
copy(dice.begin(),dice.end(),out_iter);
前两个迭代器参数指明了要复制的区间,最后一个迭代器参数指明了要将第一个元素复制到什么位置。因此,上述代码将dice容器的整个区间复制到输出流中,即显示容器的内容。
直接构建匿名迭代器作为算法参数:
copy(dice.begin(),dice.end(),ostream_iterator<int,char>(cout," "));
istream_iterator迭代器
该模板是输入迭代器概念的一个模型,同样定义在头文件iterator中。
使用两个istream_iterator对象来定义copy()的输入范围:
copy(istream_iterator<int,char>(cin),
istream_iterator<int,char>(),dice.begin());
istream_iterator也使用两个模板参数,第一个参数指出要读取的数据类型,第二个参数指出输入流使用的字符类型。
使用构造函数参数cin意味着使用由cin管理的输入流,而省略构造函数参数表示输入失败。
因此上述代码从输入流中读取,直到文件结尾、类型不匹配或出现其它输入故障为止。
reverse_iterator迭代器
该迭代器是一个反向迭代器,对其执行递增操作将导致它被递减。
通过理解vector类中的rbegin()和rend()成员函数,将有助于理解该迭代器。
rbegin()返回一个指向超尾的反向迭代器,rend()返回一个指向第一个元素的反向迭代器。因为对其执行递增操作将导致它被递减,所以可以使用下面的语句来反向显示容器的内容:
copy(dice.rbegin(),dice.rend(),out_iter); // display in reverse order
另外,必须对反向迭代器做一种特殊补偿。假设rp是一个被初始化为dice.rbegin()的反转指针。那么 *rp 是什么呢?因为rbegin()返回超尾,因此不能对该地址进行解除引用。同样,如果rend()是第一个元素的位置,则copy()必须提早一个位置停止,因为区间的结尾处不包括在区间中。
综上所述,反向指针需要通过先递减,再解除引用,方可解决问题。即如果rp指向位置6,则*rp将是位置5的值。
范例,使用reverse_iterator迭代器反向显示vector容器的内容:
vector<int> dice(10);
...
vector<int>::reverse_iterator ri;
for (ri = dice.rbegin(); ri != dice.rend(); ++ri)
cout << *ri << ' ';
back_insert_iterator迭代器
将新元素插入到容器尾部,并使用自动内存分配来确保能够容纳新的信息。
只能用于允许在尾部快速插入的容器。快速插入指的是一个时间固定的算法。具体介绍请查看<<STL 之 容器>>
为名为dice的vector<int>容器创建一个back_insert_iterator迭代器:
back_insert_iterator<vector<int> > back_iter(dice);
必须声明容器类型,因为迭代器必须使用合适的容器方法。构造函数将假设传递给它的类型有一个push_back()方法。该声明让back_iter能够使用方法push_back()来自动分配内存。
front_insert_iterator迭代器
将新元素插入到容器前端,并使用自动内存分配来确保能够容纳新的信息。
只能用于允许在起始位置做时间固定插入的容器类型。
该迭代器的声明方法与back_insert_iterator的方式相同。
insert_iterator迭代器
将新元素插入到insert_iterator构造函数的参数指定的位置前面。并使用自动内存分配来确保能够容纳新的信息。
该迭代器并没有什么插入算法限制,但速度往往较慢。
对于insert_iterator的声明,需要一个指示插入位置的构造函数参数:
insert_iterator<vector<int> > insert_iter (dice, dice.begin());