前言

虽然iostream类型不是容器,但标准库定义了可以用于这些IO类型对象的迭代器。


一、istream_iterator和ostream_iterator迭代器简介

istream_iterator读取输入流,ostream_iterator向一个输入流写数据.这些迭代器将它们对应的流当做一个特定类型的元素序列来处理.通过使用流迭代器,我们可以用泛型算法从流对象读取数据以及向其写入数据.


二、迭代器使用方法和注意事项

1.istream_iterator操作

注意: 当创建一个流迭代器时,必须指定迭代器将要读写的对象类型

  • 一个istream_iterator使用>>来读取流。因此,istream_iterator要读取的类型必须定义了输入运算符
  • 当创建一个istream_iterator时,我们可以将它绑定到一个流
  • 当然,我们还可以默认初始化迭代器,这样就创建了一个可以当做尾后值使用的迭代器

代码如下:

	istream_iterator<int> int_it(cin);//从cin读取int
	istream_iterator<int> int_eof;//尾后迭代器
	ifstream in("afile");//打开一个aflie的文件
	istream_iterator<string> str_it(in);//从"afile"读取字符串

下面是一个用istream_iterator从标准输入读取数据,存入一个vector的例子:

	istream_iterator<int> int_iter(cin);//从cin读取int
	istream_iterator<int> eof;//istream尾后迭代器
	vector<int> vec;
	while (int_iter != eof)//当有数据可供读取时
	{
		//后置递增运算符,返回迭代器的旧值
		//解引用迭代器,获得从流读取的前一个值
		vec.push_back(*int_iter++);
	}

此循环从cin读取int值,保存在vec中.
在每个循环步中,循环体代码检查in_iter是否等于eof.
eof被定义为空的istream_iterator,从而可以当做尾后迭代器来使用.
对于一个绑定到流的迭代器,一旦其关联的流遇到文件尾或遇到IO错误,迭代器的值就与尾后迭代器相等
对于传递给push_back的参数,其中用到了解引用运算符和后置递增运算符。
后置递增运算会从流中读取下一个值,向前推进,但返回的迭代器时迭代器的旧值。迭代器的旧值包含了从流中读取的前一个值,对迭代器进行解引用就能获得此值

注意: 后置递增运算会从流中读取下一个值

  • 如果没有后置递增运算,那么读取一次后就会停止,我们可以看一下测试图:
    在这里插入图片描述

  • 我们可以将程序重写为如下形式,体现istream-iterator更有用的地方:

	istream_iterator<int> int_iter(cin), eof;//从cin中读取int
	vector<int> vec(int_iter,eof);//从迭代器范围构造vec
  • 在本例中我们用一对表示元素范围的迭代器来构造vec。
  • 这两个迭代器是istream_iterator,这意味着元素的范围是通过关联的流中读取数据获得的。
  • 这个构造函数从cin中读取数据,直至遇到文件尾或者遇到一个不是int的数据类型为止.
  • 从流中读取的数据被用来构造vec。

istream_iterator操作
istream_iterator< T > in(is)in从输入流is读取类型为T的值
istream_iterator< T > end读取类型为T的值的istream_iterator迭代器,表示尾后的位置
in1==in2in1和in2必须读取相同的类型。如果他们都是尾后迭代器,或绑定到相同的输入,则两者相等
in1!=in2相等的条件不满足则为不等
*in返回从流中读取的值
in->mem与(*in).mem的含义相同
++in,in++使用元素类型所定义的>>运算符从输入流中读取下一个值。与以往一样,前置版本返回一个指向递增后的迭代器的引用,后置版本返回旧值

使用算法操作流迭代器

  • 因为算法使用迭代器来操作处理数据,而流迭代器又至少支持某些算法来操作迭代器
	istream_iterator<int> in(cin), eof;
	cout << accumulate(in, eof, 0) << endl;

在这里插入图片描述


istream_iterator可以使用懒惰求值

  • 当我们将一个istream_iterator绑定到一个流时,标准库并不保证迭代器立即从流读取数据。
  • 具体实现可以推迟从流中读取数据,直到我们使用迭代器时才真正读取。
  • 标准库中的实现所保证的是,在我们第一次解引用迭代器之前,从流中读取数据的操作已经完成了。
  • 对于大多数程序员来说,立即读取还是推迟读取没什么差别。但是,如果我们创建一个istream_iterator,没有使用就销毁了,或者我们正在从两个不同的对象同步读取一个流,那么何时读取可能就很重要了。

2.ostream_iterator操作

  • 我们可以对任何具有输出运算符(<<运算符)的定义ostream_iterator.
  • 当创建一个ostream_iterator时,我们可以提供(可选的)第二参数,它是一个字符串,在输出每个元素后都会打印此字符串。此字符串必须是一个c风格的字符串(即,一个字符串字面常量或者指向一个空字符结尾的字符数组指针)。
  • 必须将ostream_iterator绑定到一个指定的流,不能够存在空的或表示尾后位置的ostream_iterator

ostream_iterator操作
ostream_iterator< T > out(os)out将类型为T的值写到输出流os中
ostream_iterator< T > out(os,d)out将类型为T的值写到输出流os中,每个值后面都输出一个d。d指向一个空字符结尾的字符数组
out=val用<<运算符将val写入到out所绑定的ostream中,val的类型必须与out可写的类型兼容
*out,++out,out++这些运算符是存在的,但不对out做任何事。每个运算符都返回out

  • 我们可以用ostream_iterator来输出值的序列:

代码如下:

	vector<int> vec(10, 1);
	ostream_iterator<int> out_iter(cout, " ");
	for (auto e : vec)
		*out_iter++ = e;//赋值语句实际上将元素写到cout
	cout << endl;

在这里插入图片描述

  • 此程序将vec中的每个元素写到cout,每个元素后加一个空格。
  • 每次向out_iter赋值时,写操作就会被提交
  • 值得注意的是,当我们向out_iter赋值时,可以忽略解引用和递增运算。即,循环可以重写成下面的样子:
	vector<int> vec(10, 1);
	ostream_iterator<int> out_iter(cout, " ");
	for (auto e : vec)
		out_iter= e;//赋值语句实际上将元素写到cout
	cout << endl;

注意: 运算符*和++实际上对ostream_iterator对象不做任何事情,因此忽略它们对我们的程序没有任何影响。
但是,推荐第一种写法,在这种写法中,流迭代器的使用和其他迭代器的使用保持一致。如果想要将此循环改为操作其他迭代器的类型,修改起来非常容易.而且,对于读者来说,此循环的行为也更为清晰。


  • 可以调用copy来打印vec中的元素,这比编写循环更为简单:
	vector<int> vec(10, 1);
	ostream_iterator<int> out_iter(cout, " ");
	copy(vec.begin(), vec.end(), out_iter);
	cout << endl;

在这里插入图片描述

总结

以上就是今天要讲的内容,但是额外补充一点,我们还可以使用流迭代器处理类类型,前提是类型定义了>>和<<运算符。

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐