zoukankan      html  css  js  c++  java
  • 深入探索迭代器

    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,每个复制的值后面输出一个空格。

  • 相关阅读:
    uTenux-OS-Task再探
    uTenux——LED驱动讲解
    uTenux——HelloWord
    uTenux——重新整理底层驱动库
    template的超级bug
    [LeetCode] Integer to Roman
    [LeetCode] Roman to Integer
    [LeetCode]Flatten Binary Tree to Linked List
    [LeetCode] LRU Cache [Forward]
    [LeetCode] Reorder List
  • 原文地址:https://www.cnblogs.com/yshl-dragon/p/3178267.html
Copyright © 2011-2022 走看看