文章目录
- 前言
- 插入迭代器
- inserter
- front_inserter
- back_inserter
- iostream迭代器
- istream_iterator 读取输入流
- istream_iterator允许使用懒惰求值
- ostream_iterator操作
- 反向迭代器
- reverse_iterator的base成员函数
前言
除了为每个容器定义的迭代器之外,标准库在头文件iterator中还定义了额外几种迭代器。这些迭代器包括以下几种。
- 插入迭代器(insert iterator):这些迭代器被绑定到一个容器上,可用来向容器插入元素。
- 流迭代器(stream iterator):这些迭代器被绑定到输入或输出流上,可用来遍历所关联的IO流。
- 反向迭代器(reverse iterator):这些迭代器向后而不是向前移动。除了forward_list之外的标准库容器都有反向迭代器。
- 移动迭代器(move iterator):这些专用的迭代器不是拷贝其中的元素,而是移动它们。
插入迭代器
插入器有三种类型,差异在于元素插入的位置:
- back_inserter创建一个使用push_back的迭代器。
- front_inserter创建一个使用push_front的迭代器。
- inserter创建一个使用insert的迭代器。此函数接受第二个参数,这个参数必须是一个指向给定容器的迭代器。元素将被插入到给定迭代器所表示的元素之前。
inserter
当调用inserter(c,iter)
时,我们得到一个迭代器,接下来使用它时,会将元素插入到iter原来所指向的元素之前的位置。即,如果it是由inserter生成的迭代器,则下面这样的赋值语句:
*it = val;
等价于:
it = c.insert(it, val); // it指向新加入的元素val
++it; // 递增it使其指向原来的元素
front_inserter
一直向首元素位置添加元素,添加后指向新的首元素。
以以下实例区分inserter和front_inserter的区别:
back_inserter
向容器尾部不断添加元素。
以unique_copy函数为实例来观察back_inserter的作用:
值得注意的是,与unique一样,unique_copy也要求在源容器中重复元素是相邻存放的,因此容器如果无序且重复元素未相邻存放,unique_copy会失败,稳妥起见应该先对vector排序。
vector<int> vi = {2, 6, 7, 13, 15, 20, 22, 23, 25, 30};list<int> li;ostream& os(cout);int n = 10;while(n--){vi.push_back(n);}sort(vi.begin(), vi.end());for(auto i : vi){os << i << ends;}os << endl;unique_copy(vi.begin(), vi.end(), back_inserter(li));for(auto i : li){os << i << ends;}os << endl;
输出结果:
iostream迭代器
虽然iostream类型不是容器,但是标准库定义了可以用于这些IO类型对象的迭代器。
istream_iterator 读取输入流
流迭代器不支持递减运算,因为不可能在一个流中反向移动。
istream_iterator使用>>来读取流。因此,istream_iterator要读取的类型必须定义了输入运算符。
- 当创建一个istream_iterator时,可以将它绑定到一个流
- 默认初始化相当于创建一个可以当作尾后值使用的迭代器。
用istream_iterator从标准输入读取数据,存入一个vector:
istream_iterator可以用来构建容器:
istream_iterator<int> in_iter(cin);
istream_iterator<int> eof;
vector<int> vec(in_iter, eof);
本例中我们用一对表示元素范围的迭代器来构造vec。这两个迭代器是istream_iterator,这意味着元素范围是通过从关联的流中读取数据获得的。这个构造函数从cin中读取数据,直至遇到文件尾或者遇到一个不是int的数据为止。从流中读取的数据被用来构造vec。
istream_iterator允许使用懒惰求值
当我们将一个istream_iterator绑定到一个流时,标准库并不保证迭代器立即从流读取数据。 具体实现时可以推迟从流中读取数据的时机——直到我们使用迭代器时才真正读取。标准库中的实现所保证的是,在我们第一次解引用迭代器之前,从流中读取数据的操作已经完成了。 对于大多数程序来说,立即读取还是推迟读取没什么差别。但是,如果我们创建了一个istream_iterator,没有使用就销毁了,或者我们正在从两个不同的对象同步读取同一个流,那么何时读取可能就很重要了。
ostream_iterator操作
可以对任何具有输出运算符(<<运算符)的类型定义ostream_iterator。
当创建一个ostream_iterator时,我们可以提供(可选的)第二参数,它是一个字符串,在输出每个元素后都会打印此字符串。此字符串必须是一个C风格字符串(即,一个字符串字面常量或者一个指向以空字符结尾的字符数组的指针)。
与iostream_iterator不同的是:必须将ostream_iterator绑定到一个指定的流,不允许空的或表示尾后位置的ostream_iterator。
可以用ostream_iterator来输出值的序列:
此程序将vec中的每个元素写到cout,每个元素后加一个空格。每次向out_iter赋值时,写操作就会被提交。
值得注意的是,当我们向out_iter赋值时,可以忽略解引用和递增运算。即,循环可以重写成下面的样子:
istream_iterator<int> in_iter(cin);
istream_iterator<int> eof;
vector<int> vec(in_iter, eof);
ostream_iterator<int> out_iter(cout, " ");
for(auto i : vec){out_iter = i;
}
cout << endl;
运算符*和++实际上对ostream_iterator对象不做任何事情,因此忽略它们对我们的程序没有任何影响。 但是,推荐第一种形式。在这种写法中,流迭代器的使用与其他迭代器的使用保持一致。如果想将此循环改为操作其他迭代器类型,修改起来非常容易。而且,对于读者来说,此循环的行为也更为清晰。
可以通过调用copy来打印vec中的元素,这比编写循环更为简单:
copy(vec.begin(), vec.end(), out_iter);
cout << endl;
可以用unique_copy代替copy只打印不重复元素:
反向迭代器
反向迭代器就是在容器中从尾元素向首元素反向移动的迭代器。对于反向迭代器,递增(以及递减)操作的含义会颠倒过来。递增一个反向迭代器(++it)会移动到前一个元素;递减一个迭代器(- -it)会移动到下一个元素。
反向迭代器逆序打印vector<int>
中的元素:
对照普通迭代器可以看出,rbegin指向尾元素,rend指向首元素前,递增和递减操作的含义会颠倒过来:反向迭代器的递增操作会移动到前一个元素。
反向迭代器也有十分方便的时候,比如不加额外参数就可以使得元素降序排列:
reverse_iterator的base成员函数
base成员函数用来将反向迭代器转换成其对应的普通迭代器(当然反过来也是可以的,普通迭代器转换成对应的反向迭代器):
图中的对象显示了普通迭代器与反向迭代器之间的关系。例如,rcomma和rcomma.base()指向不同的元素,line.crbegin和line.cend()也是如此。这些不同保证了元素范围无论是正向处理还是反向处理都是相同的。
从技术上讲,普通迭代器与反向迭代器的关系反映了左闭合区间的特性。关键点在于[line.crbegin(),rcomma)
和[rcomma.base(),line.cend())
指向line中相同的元素范围。为了实现这一点,rcomma和rcomma.base()必须生成相邻位置而不是相同位置,crbegin()和cend()也是如此。