迭代器适配器: 流迭代器
2023年12月25日 2023年12月26日
stream iterator
iostream类型不是容器. 但标准库定义了可以用于IO类型对象的迭代器
流迭代器将流当作一个特定类型的元素序列来处理
通过流迭代器,我们可以在泛型算法中从流对象读取数据, 或者向其写入数据
两种流迭代器
类型 | 用途 |
---|---|
istream_iterator | 读取输入流 |
ostream_iterator | 向输出流写数据 |
创建流迭代器对象时, 需给出其读写的元素类型
istream_iterator要求元素类型重载了输入运算符 >>
ostream_iterator要求元素类型重载了输出运算符 <<
流迭代器不支持递减运算: 不可能在一个流中反向移动
输入流迭代器
istream_iterator
使用输入运算符 >>
从输入流读取数据
-
绑定输入流
1istream_iterator<T> stream_it(is);
-
判断数据是否读取完毕: 对输入流迭代器进行默认初始化(不绑定输入流), 该迭代器为尾后迭代器
1istream_iterator<T> stream_eof;
输入流迭代器支持的操作
-
默认构造函数
1istream_iterator<T> end; // 迭代器从流中读取类型为T的元素, 指示尾后位置
-
绑定输入流的构造函数
1istream_iterator<T> in(is); // 将迭代器绑定到输入流is. in从输入流is读取类型为T的对象
-
相等运算符和不等运算符
1in1 == in2; 2 3in1 != in2; // 可以用来判断数据是否读取完毕
-
解引用运算符
在对迭代器执行解引用运算符之前, 迭代器已从流中读取对象对非尾后迭代器执行解引用操作一定可以得到一个对象
返回迭代器当前在流中所指对象的引用
1*in;
-
前置/后置递增运算符
使用元素类型所定义的输入运算符<<
从输入流中读取下一个对象前置递增运算符的运算结果, 为迭代器递增后的引用, 是个左值; 对其进行解引用操作, 得到迭代器递增后所指对象的引用
后置递增运算符的运算结果, 为迭代器递增前的拷贝, 是个右值; 对其进行解引用操作, 得到迭代器递增前所指对象的引用
对迭代器执行递增操作, 使迭代器读取下一个对象, 或预备(随时可以)读取下一个对象
1++in; 2in++;
-
成员访问运算符
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
示例
-
创建输入流迭代器并绑定
1istream_iterator<int> int_it(cin); 2// 创建流迭代器,该迭代器读取int类型数据,且绑定了cin对象
-
创建用于判断数据是否读取完毕的输入流迭代器
1istream_iterator<int> int_eof; 2// 创建流迭代器,该迭代器读取int类型数据,作为尾后迭代器,可以判断元素是否已读取完毕
-
绑定文件输入流
1ifstream in("afile"); 2// ifstream继承自istream 3 4istream_iterator<string> str_it(in); 5// 创建流迭代器,该迭代器读取string类型数据,绑定流对象in
-
从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// 前置递增/递减运算符的优先级高于后置递增/递减运算符; 解引用运算符的优先级与后置递增/递减运算符的优先级相同
-
用一对输入流迭代器来构造容器
因为in_iter和eof均为流迭代器, 意味着容器中元素通过读取流迭代器绑定的输入流得到使用从流中读取的数据来构造容器
容器的构造函数从cin中读取数据,直至流迭代器对应的元素类型对象读取完毕, 或者出现其他类型对象
1istream_iterator<int> in_iter(cin), eof; 2vector<int> vec(in_iter, eof);
-
在泛型算法中使用流迭代器
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++;
对输出流迭代器使用解引用运算符和递增运算符的好处
- 与其他迭代器的使用保持一致
- 可以增加代码可读性
同插入迭代器
示例
-
输出容器元素
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;
-
和copy搭配使用
copy
使用copy打印vec中元素,比使用循环简洁得多
1copy(vec.begin(), vec.end(), out_iter); 2cout << endl;
-
和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; // 输出最后一条记录