六一的部落格


关关难过关关过,前路漫漫亦灿灿。



stream iterator

iostream类型不是容器. 但标准库定义了可以用于IO类型对象的迭代器

流迭代器将流当作一个特定类型的元素序列来处理

通过流迭代器,我们可以在泛型算法中从流对象读取数据, 或者向其写入数据

两种流迭代器

类型 用途
istream_iterator 读取输入流
ostream_iterator 向输出流写数据

创建流迭代器对象时, 需给出其读写的元素类型

istream_iterator要求元素类型重载了输入运算符 >>

ostream_iterator要求元素类型重载了输出运算符 <<

流迭代器不支持递减运算: 不可能在一个流中反向移动


输入流迭代器

istream_iterator

使用输入运算符 >> 从输入流读取数据

  1. 绑定输入流

    1istream_iterator<T> stream_it(is);
  2. 判断数据是否读取完毕: 对输入流迭代器进行默认初始化(不绑定输入流), 该迭代器为尾后迭代器

    1istream_iterator<T> stream_eof;

输入流迭代器支持的操作

  1. 默认构造函数

    1istream_iterator<T> end;            // 迭代器从流中读取类型为T的元素, 指示尾后位置
    
  2. 绑定输入流的构造函数

    1istream_iterator<T> in(is);        // 将迭代器绑定到输入流is. in从输入流is读取类型为T的对象
    
  3. 相等运算符和不等运算符

    1in1 == in2;
    2
    3in1 != in2; // 可以用来判断数据是否读取完毕
    
  4. 解引用运算符

    在对迭代器执行解引用运算符之前, 迭代器已从流中读取对象

    对非尾后迭代器执行解引用操作一定可以得到一个对象

    返回迭代器当前在流中所指对象的引用

    1*in;
  5. 前置/后置递增运算符

    使用元素类型所定义的输入运算符 << 从输入流中读取下一个对象

    前置递增运算符的运算结果, 为迭代器递增后的引用, 是个左值; 对其进行解引用操作, 得到迭代器递增后所指对象的引用

    后置递增运算符的运算结果, 为迭代器递增前的拷贝, 是个右值; 对其进行解引用操作, 得到迭代器递增前所指对象的引用

    对迭代器执行递增操作, 使迭代器读取下一个对象, 或预备(随时可以)读取下一个对象

    1++in;
    2in++;
  6. 成员访问运算符

    1in->mem;
    2
    3// <=>
    4// (*in).mem;
    

测试(in++->mem)

成员访问运算符 -> 使用左结合律, 作用于(in++)的运算结果

 1#include <iostream>
 2#include <vector>
 3
 4using namespace std;
 5
 6struct test
 7{
 8    int a;
 9    test(int aa) : a(aa) {}
10};
11
12int main()
13{
14    vector<test> vi;
15
16    vi.push_back(test(1));
17    vi.push_back(test(2));
18
19    auto it = vi.begin();
20
21    cout << it++->a << endl;
22
23    cout << it->a << endl;
24    return 0;
25}

输出如下

1
2

示例

  1. 创建输入流迭代器并绑定

    1istream_iterator<int> int_it(cin);
    2// 创建流迭代器,该迭代器读取int类型数据,且绑定了cin对象
    
  2. 创建用于判断数据是否读取完毕的输入流迭代器

    1istream_iterator<int> int_eof;
    2// 创建流迭代器,该迭代器读取int类型数据,作为尾后迭代器,可以判断元素是否已读取完毕
    
  3. 绑定文件输入流

    1ifstream in("afile");
    2// ifstream继承自istream
    3
    4istream_iterator<string> str_it(in);
    5// 创建流迭代器,该迭代器读取string类型数据,绑定流对象in
    
  4. 从cin读取数据, 并添加到容器

     1istream_iterator<int> in_iter(cin), eof;
     2
     3vector<int> vec;
     4
     5while (in_iter != eof)
     6    vec.push_back(*in_iter++); // 后置递增运算符的优先级高于解引用运算符; 递增迭代器, 返回递增前的迭代器的拷贝, 对其执行解引用, 添加到容器
     7
     8// 此处如果使用前置递增运算符, 其和解引用运算符优先级相同; 而前置递增运算符使用右结合律, 解引用运算符作用于(++_iter), 会跳过第一个元素, 并压入一个不存在的元素
     9
    10// 前置递增/递减运算符的优先级高于后置递增/递减运算符; 解引用运算符的优先级与后置递增/递减运算符的优先级相同
    
  5. 用一对输入流迭代器来构造容器

    因为in_iter和eof均为流迭代器, 意味着容器中元素通过读取流迭代器绑定的输入流得到

    使用从流中读取的数据来构造容器

    容器的构造函数从cin中读取数据,直至流迭代器对应的元素类型对象读取完毕, 或者出现其他类型对象

    1istream_iterator<int> in_iter(cin), eof;
    2vector<int> vec(in_iter, eof);
  6. 在泛型算法中使用流迭代器

    accumulate

    1istream_iterator<int> in(cin), eof;
    2// 使用了元素类型重载的输入运算符
    3
    4cout << accumulate(in, eof, 0) << endl;
    5// 使用元素类型重载的加法运算符
    6
    7// 直接对输入的数据进行累加,输出结果
    

输入流迭代器允许懒惰求值

将istream_iterator绑定到流时,标准库并不保证迭代器立即从流读取数据

具体实现可以推迟从流读取数据,直到我们使用迭代器时才真正读取

标准库中的实现所保证的是,在我们第一次解引用迭代器之前,已完成从流中读取数据的操作

对大多数程序来说,立即读取还是推迟读取没什么差别: 比如我们创建了一个istream_iterator, 没有使用就销毁了

可如果我们使用两个不同的流迭代器交替读取同一个流, 何时读取就很重要了


示例

以下示例仅供参考

 1#include <iostream>
 2#include <vector>
 3#include <iterator>
 4
 5using namespace std;
 6
 7int main()
 8{
 9    istream_iterator<int> in(cin), in2(cin), eof;
10    vector<int> vec;
11
12    int i = 0;
13
14    while(in != eof)
15    {
16        if (++i % 2 == 0)
17            cout << "in2: " << *in2++ << endl;
18        else
19            cout << "in: " << *in++ << endl;
20    }
21    return 0;
22}

输入

1 2 3 4 5 q

输出

in: 1
in2: 2
in: 3
in2: 4
in: 5

输出流迭代器

ostream_iterator

使用输出运算符 << 向输出流写入数据

必须将输出流迭代器绑定到输出流

  • 绑定输出流

    1ostream_iterator<T> out(os);        // 将迭代器绑定到os. out将类型为T的值写到输出流os中
    
  • 绑定输出流, 并设置分隔字符串

    d为C风格字符串 const char * , 或者以空字符 \0 结尾的字符数组

    输出每个元素后都会打印此字符串

    1ostream_iterator<T> out(os, d);        // 将迭代器绑定到os. out将类型为T的值写到输出流os中,使用以'\0'结尾的C风格字符串分隔
    

输出流迭代器支持的操作

往输出流写入数据

1out = val;            // 用输出运算符将val写入到out所绑定的ostream中. val类型必须与out的元素类型相容

其他

以下操作存在, 但不会对out做任何事情. 返回out

1*out;
2++out;
3out++;

对输出流迭代器使用解引用运算符和递增运算符的好处

  1. 与其他迭代器的使用保持一致
  2. 可以增加代码可读性

同插入迭代器


示例

  1. 输出容器元素

     1ostream_iterator<int> out_iter(cout, " ");
     2
     3for (auto e : vec)
     4    *out_iter++ = e;        // 解引用运算符存在,递增运算符也存在,但不会对out_iter做任何事情
     5                            // 推荐这种写法:流迭代器的使用与其他迭代器的使用保持一致
     6                            // 如果想将此循环改为操作其他迭代器类型,修改起来非常容易
     7
     8// <=>
     9// out_iter = e;
    10
    11cout << endl;
  2. 和copy搭配使用

    copy

    使用copy打印vec中元素,比使用循环简洁得多

    1copy(vec.begin(), vec.end(), out_iter);
    2cout << endl;
  3. 和for_each搭配使用

    for_each

    1ostream_iterator<int> out(cout, " ");
    2for_each(vec.begin(), vec.end(), [&out](int d) { out = d; });
    3cout << endl;

示例: 使用流迭代器处理类类型

Sales_item重载了输入/输出运算符

 1istream_iterator<Sales_item> item_iter(cin), eof;
 2
 3ostream_iterator<Sales_item> out_iter(cout, "\n");
 4
 5Sales_item sum = *item_iter++;           // 将第一笔交易记录存在sum中,使item_ter指向下一条记录
 6
 7while (item_iter != eof)
 8{
 9    if (item_iter->isbn() == sum.isbn())
10        sum += *item_iter++;             // 将当前记录加到sum上,使item_ter指向下一条记录
11    else
12    {
13        out_iter = sum;                  // 输出上一条记录
14        sum = *item_iter++;              // 更新当前记录,使item_ter指向下一条记录
15    }
16}
17
18out_iter = sum; // 输出最后一条记录

迭代器适配器: 流迭代器


stream iterator

iostream类型不是容器. 但标准库定义了可以用于IO类型对象的迭代器

流迭代器将流当作一个特定类型的元素序列来处理

通过流迭代器,我们可以在泛型算法中从流对象读取数据, 或者向其写入数据

两种流迭代器

类型 用途
istream_iterator 读取输入流
ostream_iterator 向输出流写数据

创建流迭代器对象时, 需给出其读写的元素类型

istream_iterator要求元素类型重载了输入运算符 >>

ostream_iterator要求元素类型重载了输出运算符 <<

流迭代器不支持递减运算: 不可能在一个流中反向移动


输入流迭代器

istream_iterator

使用输入运算符 >> 从输入流读取数据

  1. 绑定输入流

    1istream_iterator<T> stream_it(is);
  2. 判断数据是否读取完毕: 对输入流迭代器进行默认初始化(不绑定输入流), 该迭代器为尾后迭代器

    1istream_iterator<T> stream_eof;

输入流迭代器支持的操作

  1. 默认构造函数

    1istream_iterator<T> end;            // 迭代器从流中读取类型为T的元素, 指示尾后位置
    
  2. 绑定输入流的构造函数

    1istream_iterator<T> in(is);        // 将迭代器绑定到输入流is. in从输入流is读取类型为T的对象
    
  3. 相等运算符和不等运算符

    1in1 == in2;
    2
    3in1 != in2; // 可以用来判断数据是否读取完毕
    
  4. 解引用运算符

    在对迭代器执行解引用运算符之前, 迭代器已从流中读取对象

    对非尾后迭代器执行解引用操作一定可以得到一个对象

    返回迭代器当前在流中所指对象的引用

    1*in;
  5. 前置/后置递增运算符

    使用元素类型所定义的输入运算符 << 从输入流中读取下一个对象

    前置递增运算符的运算结果, 为迭代器递增后的引用, 是个左值; 对其进行解引用操作, 得到迭代器递增后所指对象的引用

    后置递增运算符的运算结果, 为迭代器递增前的拷贝, 是个右值; 对其进行解引用操作, 得到迭代器递增前所指对象的引用

    对迭代器执行递增操作, 使迭代器读取下一个对象, 或预备(随时可以)读取下一个对象

    1++in;
    2in++;
  6. 成员访问运算符

    1in->mem;
    2
    3// <=>
    4// (*in).mem;
    

测试(in++->mem)

成员访问运算符 -> 使用左结合律, 作用于(in++)的运算结果

 1#include <iostream>
 2#include <vector>
 3
 4using namespace std;
 5
 6struct test
 7{
 8    int a;
 9    test(int aa) : a(aa) {}
10};
11
12int main()
13{
14    vector<test> vi;
15
16    vi.push_back(test(1));
17    vi.push_back(test(2));
18
19    auto it = vi.begin();
20
21    cout << it++->a << endl;
22
23    cout << it->a << endl;
24    return 0;
25}

输出如下

1
2

示例

  1. 创建输入流迭代器并绑定

    1istream_iterator<int> int_it(cin);
    2// 创建流迭代器,该迭代器读取int类型数据,且绑定了cin对象
    
  2. 创建用于判断数据是否读取完毕的输入流迭代器

    1istream_iterator<int> int_eof;
    2// 创建流迭代器,该迭代器读取int类型数据,作为尾后迭代器,可以判断元素是否已读取完毕
    
  3. 绑定文件输入流

    1ifstream in("afile");
    2// ifstream继承自istream
    3
    4istream_iterator<string> str_it(in);
    5// 创建流迭代器,该迭代器读取string类型数据,绑定流对象in
    
  4. 从cin读取数据, 并添加到容器

     1istream_iterator<int> in_iter(cin), eof;
     2
     3vector<int> vec;
     4
     5while (in_iter != eof)
     6    vec.push_back(*in_iter++); // 后置递增运算符的优先级高于解引用运算符; 递增迭代器, 返回递增前的迭代器的拷贝, 对其执行解引用, 添加到容器
     7
     8// 此处如果使用前置递增运算符, 其和解引用运算符优先级相同; 而前置递增运算符使用右结合律, 解引用运算符作用于(++_iter), 会跳过第一个元素, 并压入一个不存在的元素
     9
    10// 前置递增/递减运算符的优先级高于后置递增/递减运算符; 解引用运算符的优先级与后置递增/递减运算符的优先级相同
    
  5. 用一对输入流迭代器来构造容器

    因为in_iter和eof均为流迭代器, 意味着容器中元素通过读取流迭代器绑定的输入流得到

    使用从流中读取的数据来构造容器

    容器的构造函数从cin中读取数据,直至流迭代器对应的元素类型对象读取完毕, 或者出现其他类型对象

    1istream_iterator<int> in_iter(cin), eof;
    2vector<int> vec(in_iter, eof);
  6. 在泛型算法中使用流迭代器

    accumulate

    1istream_iterator<int> in(cin), eof;
    2// 使用了元素类型重载的输入运算符
    3
    4cout << accumulate(in, eof, 0) << endl;
    5// 使用元素类型重载的加法运算符
    6
    7// 直接对输入的数据进行累加,输出结果
    

输入流迭代器允许懒惰求值

将istream_iterator绑定到流时,标准库并不保证迭代器立即从流读取数据

具体实现可以推迟从流读取数据,直到我们使用迭代器时才真正读取

标准库中的实现所保证的是,在我们第一次解引用迭代器之前,已完成从流中读取数据的操作

对大多数程序来说,立即读取还是推迟读取没什么差别: 比如我们创建了一个istream_iterator, 没有使用就销毁了

可如果我们使用两个不同的流迭代器交替读取同一个流, 何时读取就很重要了


示例

以下示例仅供参考

 1#include <iostream>
 2#include <vector>
 3#include <iterator>
 4
 5using namespace std;
 6
 7int main()
 8{
 9    istream_iterator<int> in(cin), in2(cin), eof;
10    vector<int> vec;
11
12    int i = 0;
13
14    while(in != eof)
15    {
16        if (++i % 2 == 0)
17            cout << "in2: " << *in2++ << endl;
18        else
19            cout << "in: " << *in++ << endl;
20    }
21    return 0;
22}

输入

1 2 3 4 5 q

输出

in: 1
in2: 2
in: 3
in2: 4
in: 5

输出流迭代器

ostream_iterator

使用输出运算符 << 向输出流写入数据

必须将输出流迭代器绑定到输出流

  • 绑定输出流

    1ostream_iterator<T> out(os);        // 将迭代器绑定到os. out将类型为T的值写到输出流os中
    
  • 绑定输出流, 并设置分隔字符串

    d为C风格字符串 const char * , 或者以空字符 \0 结尾的字符数组

    输出每个元素后都会打印此字符串

    1ostream_iterator<T> out(os, d);        // 将迭代器绑定到os. out将类型为T的值写到输出流os中,使用以'\0'结尾的C风格字符串分隔
    

输出流迭代器支持的操作

往输出流写入数据

1out = val;            // 用输出运算符将val写入到out所绑定的ostream中. val类型必须与out的元素类型相容

其他

以下操作存在, 但不会对out做任何事情. 返回out

1*out;
2++out;
3out++;

对输出流迭代器使用解引用运算符和递增运算符的好处

  1. 与其他迭代器的使用保持一致
  2. 可以增加代码可读性

同插入迭代器


示例

  1. 输出容器元素

     1ostream_iterator<int> out_iter(cout, " ");
     2
     3for (auto e : vec)
     4    *out_iter++ = e;        // 解引用运算符存在,递增运算符也存在,但不会对out_iter做任何事情
     5                            // 推荐这种写法:流迭代器的使用与其他迭代器的使用保持一致
     6                            // 如果想将此循环改为操作其他迭代器类型,修改起来非常容易
     7
     8// <=>
     9// out_iter = e;
    10
    11cout << endl;
  2. 和copy搭配使用

    copy

    使用copy打印vec中元素,比使用循环简洁得多

    1copy(vec.begin(), vec.end(), out_iter);
    2cout << endl;
  3. 和for_each搭配使用

    for_each

    1ostream_iterator<int> out(cout, " ");
    2for_each(vec.begin(), vec.end(), [&out](int d) { out = d; });
    3cout << endl;

示例: 使用流迭代器处理类类型

Sales_item重载了输入/输出运算符

 1istream_iterator<Sales_item> item_iter(cin), eof;
 2
 3ostream_iterator<Sales_item> out_iter(cout, "\n");
 4
 5Sales_item sum = *item_iter++;           // 将第一笔交易记录存在sum中,使item_ter指向下一条记录
 6
 7while (item_iter != eof)
 8{
 9    if (item_iter->isbn() == sum.isbn())
10        sum += *item_iter++;             // 将当前记录加到sum上,使item_ter指向下一条记录
11    else
12    {
13        out_iter = sum;                  // 输出上一条记录
14        sum = *item_iter++;              // 更新当前记录,使item_ter指向下一条记录
15    }
16}
17
18out_iter = sum; // 输出最后一条记录