泛型3(迭代器)

除了为每个容器定义的迭代器之外,标准库在头文件 iterator 中还定义了额外几种迭代器。这些迭代器包括以下几种:

插入迭代器:这些迭代器被绑定到一个容器上,可用来向容器插入元素

流迭代器:这些迭代器被绑定到输入或输出流上,可用来变量所有管理的 IO 流

反向迭代器:这些迭代器向后而不是向前移动。除了 forward_list 之外的标准库容器都有反向迭代器

移动迭代器:这些专用的迭代器不是拷贝其中的元素,而是移动它们。

 

插入迭代器(back_inserter/front_inserter/inserter):

 1 #include <iostream>
 2 #include <algorithm>
 3 #include <iterator>    
 4 #include <list>
 5 using namespace std;
 6 
 7 int main(void){
 8     list<int> lst = {1, 2, 3, 4, 5};
 9     list<int> lst1, lst2, lst3;//空list
10     
11     copy(lst.cbegin(), lst.cend(), front_inserter(lst1));
12     for(const auto &indx : lst1){
13         cout << indx << " ";
14     }
15     cout << endl;//5 4 3 2 1
16 
17     copy(lst.cbegin(), lst.cend(), back_inserter(lst2));
18     for(const auto &indx : lst2){
19         cout << indx << " ";
20     }
21     cout << endl;//1 2 3 4 5
22 
23     copy(lst.cbegin(), lst.cend(), inserter(lst3, lst3.begin()));
24     for(const auto &indx : lst3){
25         cout << indx << " ";
26     }
27     cout << endl;//1 2 3 4 5
28 
29     return 0;
30 }
View Code

注意:它们是迭代器适配器。back_inserter、front_inserter 和 inserter 分别会调用 push_back、push_front 和 insert,因此只有在容器支持 push_back 的情况下我们才可以使用 back_inserter,容器支持 push_front 的情况下我们才可以使用 front_inserter,容器支持 insert 的情况下才可以调用 inserter

在插入过程中,back_inserter 总是将新元素插入当前容器尾后迭代器之前,front_inserter 总是将新元素插入当前迭代器首元素之前的位置。而当调用 inserter(c, iter) 时,我们得到一个迭代器,接下来使用它时,会将元素插入到 iter 原来所指向的元素之前的位置。即,如果 it 是由 inserter 生成的迭代器,则:

*it = val;

其效果于下下面代码一样:

it = c.insert(it, val);// it 指向新加入的元素

++it;// 递增 it 使他指向原来的元素

 

iostream 迭代器:

虽然 iostream 不是迭代器,但标准库定义了可以用于这些 IO 类型对象的迭代器。istream_iterator 读取输入流,ostream_iterator 向一个输出流写数据。这些迭代器将它们对于的流当作一个特定类型的元素序列来处理。通过使用流迭代器,我们可以用泛型算法从流对象读取数据以及向其写入数据。

istream_iterator 操作:

当创建一个流迭代器时,必须指定迭代器将要读写的对象类型。一个 istream_iterator 使用 >> 来读取流。因此,istream_iterator 要读取的类型必须定义了输入运算符。反之亦然(我们可以为任何定义了输入运算符的类型创建 istream_iterator 对象)。当创建一个 istream_iterator 时,我们可以将它绑定到一个流。也可以默认初始化迭代器,这样就创建了一个可以当作尾后值使用的迭代器:

1     istream_iterator<int> int_it(cin);//从cin读取int
2     istream_iterator<int> int_eof;//尾后迭代器
3 
4     ifstream in("afile");
5     istream_iterator<string> str_it(in);//从afile读取字符串
View Code

 

用 istream_iterator 从标准输入读取数据并存入一个 vector:

 1 vector<int> v;
 2     istream_iterator<int> in_iter(cin);//从cin读取int
 3     istream_iterator<int> eof;//istream尾后迭代器
 4     while(in_iter != eof){//当有数据可读时
 5         //后置递增运算读取流,返回迭代器的旧值
 6         v.push_back(*in_iter++);//解引用迭代器,获得从流读取的前一个值
 7     }
 8     for(const auto &indx : v){
 9         cout << indx << " ";
10     }
11     cout << endl;
View Code

注意:对于一个绑定到流的迭代器,一旦其关联的流遇到文件尾或遇到 IO 错误,迭代器就与尾后迭代器相等

 

用流迭代器直接构造容器:

1 istream_iterator<int> in(cin);
2     istream_iterator<int> ed;
3     vector<int> vec(in, ed);
4     for(const auto &indx : vec){
5         cout << indx << " ";
6     }
7     cout << endl;
View Code

注意:这个构造函数从 cin 中读取数据,直至遇到文件尾或者遇到一个不是 int 的数据为止

 

使用算法操作流迭代器:

由于算法使用迭代器操作来处理数据,而流迭代器又至少支持某些迭代器的操作,因此我们至少可以使用某些算法类操作流迭代器。如,对一对 istream_iterator 来调用 accumulate:

 1 #include <iostream>
 2 #include <vector>
 3 #include <algorithm>
 4 #include <iterator>
 5 using namespace std;
 6 
 7 int main(void){
 8     istream_iterator<int> in(cin), eof;
 9     cout << accumulate(in, eof, 0) << endl;
10 
11     return 0;
12 }
View Code

 

istream_iterator 允许使用懒惰求值:

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

 

ostream_iterator 操作:

 我们可以对任何具有输出运算符的类定义 ostream_iterator。当创建一个 ostream_iterator 时,我们可以提供(可选的)第二参数,它是一个字符串,在输出每个元素后都会打印此字符串。此字符串必须是一个 c 风格字符串。必须将 ostream_iterator 绑定到一个指定的流,不允许空的或表示尾后位置的 ostream_iterator。

 

用 ostream_iterator 来输出值的序列:

 1 #include <iostream>
 2 #include <iterator>
 3 #include <vector>
 4 using namespace std;
 5 
 6 int main(void){
 7     vector<int> vec = {1, 2, 3, 4, 45, 5, 6};
 8 
 9     ostream_iterator<int> out_iter(cout, " ");
10     for(const auto &e : vec){
11         *out_iter++ = e;//赋值语句实际上将元素写到cout
12     }
13     cout << endl;//1 2 3 4 45 5 6
14 
15     return 0;
16 }
View Code

 

值得注意的是,当我们向 out_iter 赋值时,可以忽略解引用和递增运算。即,上面代码可以写成下面的形式:

 1 #include <iostream>
 2 #include <iterator>
 3 #include <vector>
 4 using namespace std;
 5 
 6 int main(void){
 7     vector<int> vec = {1, 2, 3, 4, 45, 5, 6};
 8 
 9     ostream_iterator<int> out_iter(cout, " ");
10     for(const auto &e : vec){
11         out_iter = e;//赋值语句将元素写到cout
12     }
13     cout << endl;//1 2 3 4 45 5 6
14 
15     return 0;
16 }
View Code

但是对于 istream_iterator 是不能这样操作的~

运算符 * 和 ++ 实际上对 ostream_iterator 对象不做任何事情,因此忽略它们对我们的程序没有任何影响。但是最好使用第一种写法,这此种写法中,ostream_iterator 与其它迭代器的使用保持一致。更容易修改,可读性也更好~

 

通过调用 copy 来打印容器中的元素:

 1 #include <iostream>
 2 #include <iterator>
 3 #include <array>
 4 using namespace std;
 5 
 6 int main(void){
 7     array<int, 5> a = {1, 2, 3, 4, 5};
 8     ostream_iterator<int> out(cout, " ");
 9     copy(a.begin(), a.end(), out);//将a中元素拷贝到out,实际上就是将a中元素写到cout
10     cout << endl;//1 2 3 4 5
11 
12     return 0;
13 }
View Code

 

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

我们可以为任何定义了输入运算符的类型创建 istream_iterator 对象。类似的,只要类型有输出运算符我们就可以为其定义 ostream_iterator:

 1 #include <iostream>
 2 #include <list>
 3 #include <iterator>
 4 using namespace std;
 5 
 6 class gel{
 7 friend istream& operator>>(istream&, gel&);//将>>重载为gel的友元函数
 8 friend ostream& operator<<(ostream&, const gel&);//将<<重载为gel的友元函数
 9 
10 private:
11     int x, y, z;
12 
13 public:
14     gel(int a, int b, int c) : x(a), y(b), z(c) {}
15     gel(int a, int b) : gel(a, b, 0) {}
16     gel(int a) : gel(a, 0, 0) {}
17     gel() : gel(0, 0, 0) {}
18     ~gel(){}
19 };
20 
21 istream& operator>>(istream &is, gel &it) {
22     is >> it.x >> it.y >> it.z;
23     return is;
24 }
25 
26 ostream& operator<<(ostream &os, const gel &it){
27     os << it.x << " " << it.y << " " << it.z;
28     return os;
29 }
30 
31 int main(void){
32     istream_iterator<gel> is(cin), eof;
33     ostream_iterator<gel> os(cout, "\n");
34     list<gel> l;
35 
36     while(is != eof){
37         l.push_back(*is++);//解引用迭代器,获得从流读取的前一个值
38     }
39 
40     for(const auto &indx : l) {
41         *os++ = indx;//将indx拷贝赋值给os,即将indx写到cout
42     }
43     cout << endl;
44 
45     return 0;
46 }
View Code

注意:只有重载了 >> / << 的类类型才能创建 istream_iterator 或 ostream_iterator 对象

 

用流迭代器读写文件:

 1 #include <iostream>
 2 #include <fstream>
 3 #include <vector>
 4 #include <iterator>
 5 #include <algorithm>
 6 using namespace std;
 7 
 8 const char *filename = "D:\\code\\c++\\ac29.txt";
 9 
10 int main(void){
11 
12     // 从屏幕读到vec中
13     istream_iterator<string> in(cin), in_eof;
14     vector<string> vec(in, in_eof);
15 
16     //从vec写到文件ac29.txt中
17     ofstream out_file(filename);//用只写的方式打开文件
18     ostream_iterator<string> out_file_it(out_file, " ");
19     for(const auto &indx : vec){
20         *out_file_it++ = indx;
21     }
22 
23     out_file.close();//后面还要对filename文件进行操作,所以一定要关闭out_file文件流
24 
25     //从文件ac29.txt中读到ved中
26     ifstream in_file(filename);//用只读的方式打开文件
27     istream_iterator<string> in_file_it(in_file), in_file_eof;
28     vector<string> ved(in_file_it, in_file_eof);
29 
30     in_file.close();//养成用完就关闭文件流的好习惯~
31 
32     //比较vec和ved中的内容是否相同
33     cout << equal(vec.cbegin(), vec.cend(), ved.cbegin()) << endl;
34     return 0;  
35 }
View Code

 

posted @ 2018-01-25 20:35  geloutingyu  阅读(445)  评论(0编辑  收藏  举报