深入探索迭代器

0. 概述总览】

之前已强调标准库所定义的迭代器不依赖于特定的容器。
事实上,C++ 语言还提供了另外 3 种迭代器:

插入迭代器:    与容器绑定在一起,实现在容器中插入元素的功能。 

iostream 迭代器: 可与输入或输出流绑定在一起,用于迭代遍历所关联的 IO 流。

反向迭代器:     实现向后遍历,而不是向前遍历。所有容器类型都定义了自己的 reverse_iterator 类型,
          由 rbegin 和 rend 成员函数返回。

上述迭代器类型都在 iterator 头文件中定义。

1. 插入迭代器】

通过插入迭代器赋值时,迭代器将会插入一个新的元素。
C++ 语言提供了 3 种插入器,其差别在于插入元素的位置不同。

back_inserter,创建使用 push_back 实现插入的迭代器。

front_inserter,使用 push_front 实现插入。

inserter,使用 insert 实现插入操作。

front_inserter 的操作类似于 back_inserter:
该函数将创建一个迭代器,调用它所关联的基础容器的 push_front 成员函数代替赋值操作。

注意:在 vector 或其他没有 push_front 运算的容器上使用 front_inserter,将产生错误。

inserter 适配器提供更普通的插入形式。有两个实参:
所关联的容器和指示起始插入位置的迭代器。

// position an iterator into ilst
list<int>::iterator it = find (ilst.begin(), ilst.end(), 42);
// insert replaced copies of ivec at that point in ilst
replace_copy (ivec.begin(), ivec.end(), inserter (ilst, it), 100, 0);

首先用 find 定位 ilst 中的某个元素。
inserter 将会在 ilst 中由 find 返回的迭代器所指向的元素前面插入新元素。
最后调用 replace_copy 从 ivec 中复制元素,并将其中值为 100 的元素替换为 0 值。
ilst 的新元素在 it 所标明的元素前面插入。
在创建 inserter 时,应指明新元素在何处插入。
inserter 函数总是在它的迭代器实参所标明的位置前面插入新元素。

inserter 的行为与 front_inserter 的有很大差别:
在使用 front_inserter 时,元素始终在容器的第一个元素前面插入。
而使用 inserter 时,元素则在指定位置前面插入。
即使此指定位置初始化为容器中的第一个元素,
但是,一旦在该位置前插入一个新元素后,插入位置就不再是容器的首元素了:

list<int> ilst, ilst2, ilst3;     // empty lists

// after this loop ilst contains: 3 2 1 0
for (list<int>::size_type i = 0; i != 4; ++i)
    ilst.push_front(i);

// after copy ilst2 contains: 0 1 2 3
copy (ilst.begin(), ilst.end(), front_inserter(ilst2));

// after copy, ilst3 contains: 3 2 1 0
copy (ilst.begin(), ilst.end(), inserter (ilst3, ilst3.begin()));

注意 front_inserter 的使用将导致元素以相反的次序出现在目标对象中。

2. iostream 迭代器】

虽然 iostream 类型不是容器,但标准库同样提供了在 iostream 对象上使用的迭代器:
istream_iterator 用于读取输入流,而 ostream_iterator 则用于写输出流。
这些迭代器将它们所对应的流视为特定类型的元素序列。
使用流迭代器时,可以用泛型算法从流对象中读数据(或将数据写到流对象中)。

// 表 11.1 iostream 迭代器的构造函数
istream_iterator<T> in(strm);
         创建从输入流 strm 中读取 T 类型对象的 istream_iterator 对象

istream_iterator<T> in;
         istream_iterator 对象的超出末端迭代器

ostream_iterator<T> in(strm);
         创建将 T 类型的对象写到输出流 strm 的 ostream_iterator 对象

ostream_iterator<T> in(strm, delim);
         创建将 T 类型的对象写到输出流 strm 的 ostream_iterator 对象,

         在写入过程中使用 delim 作为元素的分隔符。

         delim 是以空字符结束的字符数组

流迭代器只定义了最基本的迭代器操作:自增、解引用和赋值。
此外,可比较两个 istream 迭代器是否相等(或不等),而 ostream 迭代器则不能。

// 表 11.2. istream_iterator 的操作
it1 == it2 和 it1 != it2
         比较两上 istream_iterator 对象是否相等(不等)。
         迭代器读取的必须是相同的类型。如果两个迭代器都是 end 值,则它们相等。
         对于两个都不指向流结束位置的迭代器,如果它们使用同一个输入流构造,则它们相等

*it      返回从流中读取的值

it->mem  (*it).mem 的同义语。返回从流中读取的对象的 mem 成员

++it 和 it++
         通过使用元素类型提供的 >> 操作从输入流中读取下一个元素值,使迭代器向前移动。
         通常,前缀版本使用迭代器在流中向前移动,并返回对加 1 后的迭代器的引用。
         而后缀版本使迭代器在流中向前移动后,返回原值

2.1 流迭代器的定义

流迭代器都是类模板:
任何已定义输入(输出)操作符的类型都可以定义 istream_iterator (ostream_iterator)。
在创建流迭代器时,必须指定迭代器所读写的对象类型:

istream_iterator<int> cin_it(cin);    // reads ints1 from cin
istream_iterator<int> end_of_stream;  // end iterator value
// writes Sales_items from the ofstream named outfile // each element is followed by a space ofstream outfile; ostream_iterator<Sales_item> output(outfile, " ");

ostream_iterator 对象必须与特定的流绑定在一起。
在创建 istream_iterator 时,可直接将它绑定到一个流上。
另一种方法是在创建时不提供实参,则该迭代器指向超出末端位置。
然而,ostream_iterator 不提供超出末端迭代器。
在创建 ostream_iterator 对象时有个可选的第二参数,指定将元素写入输出流时使用的分隔符。
分隔符必须是 C 风格字符串,必须以空字符结束;否则,其行为将是未定义的。

2.2 istream_iterator 对象上的操作

构造与流绑定在一起的 istream_iterator 对象时将对迭代器定位,
以便第一次对该迭代器进行解引用时即可从流中读取第一个值。
考虑下面例子,可使用 istream_iterator 对象将标准输入读到 vector 对象中。

istream_iterator<int> in_iter(cin); // read ints from cin
istream_iterator<int> eof; // istream "end" iterator

// read until end of file, storing what was read in vec
while (in_iter != eof)
     // increment advances the stream to the next value
     // dereference reads next value from the istream
     vec.push_back(*in_iter++);

对 istream_iterator 对象做自增运算使该迭代器在流中向前移动。
然而,使用后自增运算的表达式,其结果是迭代器原来的值。
自增的效果是使迭代器的流中移动到下一个值,但返回指向前一个值的迭代器。
对该迭代器进行解引用获取该值。更有趣的是可以这样重写程序:

istream_iterator<int> in_iter(cin); // read ints from cin
istream_iterator<int> eof;      // istream "end" iterator
vector<int> vec(in_iter, eof);  // construct vec from an iterator range

这里,用一对标记元素范围的迭代器构造 vec 对象。
意味着这段范围的元素是通过读取所关联的流来获得的。
这个构造函数的效果是读 cin,直到到达文件结束或输入的不是 int 型数值为止。

2.3 ostream_iterator 对象和 ostream_iterator 对象的使用

可使用 ostream_iterator 对象将一个值序列写入流中,
其操作的过程与使用迭代器将一组值逐个赋给容器中的元素相同:

// write one string per line to the standard output
ostream_iterator<string> out_iter(cout, "
");
// read strings from standard input and the end iterator
istream_iterator<string> in_iter(cin), eof;

// read until eof and write what was read to the standard output
while (in_iter != eof)
    // write value of in_iter to standard output
    // and then increment the iterator to get the next value from cin
   *out_iter++ = *in_iter++;

这个程序读 cin,并将每个读入的值依次写到 cout 中不同的行中。
首先,定义一个 ostream_iterator 对象,
用于将 string 类型的数据写到 cout 中,每个 string 对象后跟一个换行符。
定义两个 istream_iterator 对象,用于从 cin 中读取 string 对象。
while 循环是将读取的数据赋给 out_iter,从而输出到 cout 上。
这个赋值类似于将一个数组复制给另一个数组的程序。
对这两个迭代器进行解引用,将右边的值赋给左边的元素,然后两个迭代器都自增 1。
效果是:将读取的数据输出到 cout 上,然后两个迭代器都加 1,再从 cin 中读取下一个值。

2.4 在类类型上使用 istream_iterator

提供了输入操作符(>>)的任何类型都可以创建 istream_iterator 对象。
例如,可如下使用 istream_iterator 对象读取一系列的 Sales_iter 对象,并求和:

istream_iterator<Sales_item> item_iter(cin), eof;
Sales_item sum; // initially empty Sales_item
sum = *item_iter++; // read first transaction into sum and get next record

while (item_iter != eof) {
    if (item_iter->same_isbn(sum))
        sum = sum + *item_iter;
    else {
        cout << sum << endl;
        sum = *item_iter;
    }
    ++item_iter; // read next transaction
}

cout << sum << endl; // remember to print last set of records

该程序将迭代器 item_iter 与 cin 绑在一起,意味着迭代器将读取 Sales_item 类型的对象。
然后给迭代器加 1,使流从标准输入中读取下一记录。
while 循环反复执行直到到达 cin 的结束位置为止。
while 中的第一个语句对 istream 迭代器进行解引用,获得最近读入的对象。
循环的最后一步是给迭代器加 1,在本例中,将导致从标准输入中读入下一个 Sales_item 对象。
在结束程序之前,还要输出从输入中读入的最后一个 ISBN 所关联的值。

2.5 流迭代器的限制

流迭代器有下面几个重要的限制:
不可能从 ostream_iterator 对象读入,也不可能写到 istream_iterator 对象中。
一旦给 ostream_iterator 对象赋了一个值,写入就提交了。赋值后不能再改变这个值。
此外,ostream_iterator 对象中每个不同的值都只能正好输出一次。
ostream_iterator 没有 -> 操作符。

2.6 与算法一起使用流迭代器

算法是基于迭代器操作实现的,流迭代器至少定义了一些迭代器操作。
因此,至少可在一些泛型算法上使用这类迭代器。
考虑下面的例子,从标准输入读取一些数,再将读取的不重复的数写到标准输出:

istream_iterator<int> cin_it(cin);    // reads ints from cin
istream_iterator<int> end_of_stream;  // end iterator value

// initialize vec from the standard input:
vector<int> vec(cin_it, end_of_stream);
sort(vec.begin(), vec.end());

// writes ints to cout using " " as the delimiter
ostream_iterator<int> output(cout, " ");

// write only the unique elements in vec to the standard output
unique_copy(vec.begin(), vec.end(), output);

如果程序的输入是:23 109 45 89 6 34 12 90 34 23 56 23 8 89 23
输出则是:6 8 12 23 34 45 56 89 90 109

程序用一对迭代器 input 和 end_of_stream 创建了 vec 对象。
这个初始化的效果是读取 cin 直到文件结束或者出现错误为止。
读取的值保存在 vec 里。读取输入和初始化 vec 后,调用 sort 对输入的数排序。
sort 调用完成后,重复输入的数就会相邻存储。
再使用 unique_copy 算法,将输入范围中不重复的值复制到目标迭代器。
该调用将输出迭代器用作目标。
最后将 vec 中不重复的值复制给 cout,每个复制的值后面输出一个空格。

原文地址:https://www.cnblogs.com/yshl-dragon/p/3178267.html