容器一般都会提供一个迭代器,用于跟踪元素操作。 但是其实,标准库所定义的迭代器不依赖于特定的容器。 而容器所提供的迭代器也只是,标准库提供的一下三种迭代器的一种:
1 插入迭代器: 这类迭代器与容器绑定在一起,实现在容器中插入元素的功能
2 iostream 迭代器: 这类迭代器可与输入或输出流绑定在一起, 用于迭代遍历所关联的IO流
3 反向迭代器: 这类迭代器实现向后遍历, 而不是向前遍历。
所有的容器都定义了自己的reverse_iterator 类型,由
rbegin 和 rend
成员函数返回。
迭代器的使用 都在iterator头文件中定义。
下面介绍,如何在泛型算法中 使用这些迭代器, 并如何利用const_iterator容器。以及总结5中迭代器。
1
插入迭代器:
一般有三种插入迭代器:
back_inserter 创建使用push_back实现插入的迭代器
front_inserter 使用push_front实现插入
这两个迭代器,都是接受一个容器实参的。
inserter 使用inserter 实现插入操作。 除了所关联的容器外,还接受第二个实参, 指向插入起始位置的迭代器。
它们的用法:
front_inserter与back_inserter的用法相同,都是在指定的地方插入元素
inserter将产生在指定位置实现插入的迭代器:
list<int>::iterator it = find(ilst.begin(), ilst.end(), 42):
replace_copy(ivec.begin(), ivec.end(), inserter(ilst, it), 100, 0);
这段代码的含义是: 在ilst中 在第一个出现42的地方,插入ivec中begin到end之间的元素,并将这一段元素中的100替换为0。
inserter迭代器和copy及replace算法结合的用处是 , 将一个容器中的元素插入到另一个容器中,并实现是否替换功能。
2
iostream 迭代器
虽然iostream 不是容器。 但同样提供了迭代器:
istream_iterator 用于读取输入流, 而ostream_iterator 则用于写输出流。 这些迭代器将它们所对应的流视为特定类型的元素序列。
使用流迭代器时,可以用泛型算法从流对象中读数据(或将数据写到流对象中)。
iostream 迭代器的构造函数:
istream_iterator<T> in(strm); 表示从输入流strm中读取T类型对象的istream_iterator对象。
istream_iterator<T> in; istream_iterator对象的超出末端迭代器
ostream_iterator<T> in(strm); 解释同istream_iterator
ostream_iterator<T> in(strm, delim); 创建T类型的对象写到输出流strm的ostream_iterator 对象, 在写入过程中使用delim作为元素的分隔符。delim是以空字符结束的字符数组。
流迭代器只定义了最基本的迭代器操作:自增、解引用和赋值。 此外,还提供了一些比较运算(相等或不等, 输出流则没有提供此类操作)。
流迭代器的定义:
流迭代器都是类模板, 任何已定义的输入输出操作符(如>>)的类型(如cin)都可以定义 istream_iterator, 类似地可以定义ostream_iterator。
在构造函数中的空格符必须是C风格字符串,因此该字符串必须以空字符结束, 否则, 其行为将是未定义的。
istream_iterator对象上的操作:
举个例子,便一目了然:
istream_iterator<int> in_iter(cin);
istream_iterator<int> eof;
while(in_iter!=eof)
vec.push_back(*in_iter++);
其中while循环如何结束。 在这里,cin输入流是通过关联文件或输入来获得的, 当到达文件结尾或 输入的不是int 型数值 时, while停止。
ostream_iterator对象和 ostream_iterator对象的使用:
可使用ostream_iterator对象将一个值序列写入流中, 其操作的过程与使用迭代器将一组值逐个赋给容器中的元素相同:
ostream_iterator<string> in_iter(cout, "\n"); 表面以回车为间隔
istream_iterator<string> in_iter(cin), eof;
while(in_iter != eof)
{
*out_iter++ = *in_iter++;
}
看到这里,想到了文件输入输出操作,貌似有几分类似:
ifstream infile;
infile.open("in");
ofstream outfile;
oufile.open("out");
即他们都要先申明是输入还是输出, 然后再绑定某一个文件/流。
在类类型上使用 istream_iterator
输出的流迭代器的解引用会立即 输出到指定的输数流上 或者屏幕或者文本。
即利用类模板的性质, 流迭代器指向的元素和解引用的值都是该类类型。 然后 就可以通过流迭代器找到 该类的元素,通过该元素调用该类的成员。
一定要把流迭代器,想成存放很多输入输出的元素容器。
流迭代器的限制:
1 不可能从 ostream_iterator对象读入, 也不可能写到 istream_iterator对象中
2 一旦给ostream_iterator 对象赋一个值, 写入就提交了。 赋值后, 没有办法再改变这个值。
3 ostream_iterator没有->操作符
与算法一起使用流迭代器:
算法是基于迭代器操作实现的。
流迭代器至少定义了一些迭代器操作。 由于流迭代器支持迭代器操作, 因此,至少可在一些泛型算法上使用这类迭代器。
反向迭代器:
反向迭代器的使用与迭代器的使用完全是反过来的, 它通过定义一对反向迭代器(rbegin 和 rend)成员来完成。
这样如果降序来排列vector, 只需向sort传递一对反向迭代器。
反向迭代器与其它迭代器之间的关系:
如果要取出一行中 最后一个单词(这些单词以逗号分隔开)
则 用反向迭代器查找 最后一个冒号(即方向的第一个冒号) 比较方便。
然后利用 line.rbegin(), rcomma之间的单词。 但是这样的输出会导致 输出的单词字母都是颠倒的。 因此,为了纠正这个问题,我们必须把 rbegin() 和 rcomma转换为普通迭代器 ,然后再进行输出:
其实,没必要转换line.rbegin(), 因为它必定对应的是 line.end(), 而对于rcomma, 只需调用多有反向迭代器类型都提供的成员函数 base 来 转换 rcomma即可。
const迭代器:
当我们不希望对迭代器的地址进行修改时, 可以使用它。 当用一些接受一对迭代器的算法或操作时, 必须使用匹配的const迭代器。
五种迭代器:
迭代器定义了常用的操作集, 但有些迭代器具有比其他迭代器更强大的功能。 例如, ostream_iterator 只支持自增、解引用和赋值运算。 而vector除了提供这些操作外,还提供自减、关系和算术运算。 因此,可以对迭代器进行分类。
类似地, 还可根据算法要求它的迭代器提供什么类型的操作, 对算法进行分类。例如,find 只要求迭代器提供读取所指向内容和自增的功能。 另一些算法,如sort 则要求其迭代器有读、写和随机访问元素的功能。
迭代器种类:
输入迭代器: 读, 不能写,只支持自增运算
还支持 相等和不等操作符, 解引用操作符, 箭头操作符
输出迭代器: 写,不能读, 只支持自增运算
还支持解引用, 而且每个迭代器的值必须正好输入一次,只能解引用一次。 一般用作算法的第三个实参。
前向迭代器: 读和写, 只支持自增运算
以一个方向遍历序列, 可以对同一个元素多次读写。 可复制前向迭代器来记录序列中的一个位置。 需要前向迭代器的泛型算法包括 replace
双向迭代器: 读和写, 支持自增和自减运算
从两个方向读写容器。双向迭代器包括前向迭代器的所有操作,除此之外, 还包括自减运算。
随机访问迭代器: 读和写, 支持完整的迭代器算术运算
需要低级类别迭代器的地方,可使用任意一种更高级的迭代器。 对于需要输入迭代器的算法, 可传递前向、双向或随机访问迭代器调用该算法。
调用需要随机访问迭代器的算法是,必须传递随机访问迭代器。
map和 set 关联容器的键是const对象,因此,关联容器不能使用任何写序列元素的算法。 只能使用与关联容器绑在一起的迭代器来提供用于读操作的实参。