zoukankan      html  css  js  c++  java
  • 泛型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, "
    ");
    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
  • 相关阅读:
    文件或目录损坏且无法读取的解决办法
    MP3/视频播放
    系统备份
    网线水晶头接法
    网站模板
    EasyNVR将如何能够把内网各种各样的监控摄像机对接到公网云平台
    EasyNVR将如何能够把内网各种各样的监控摄像机对接到公网云平台
    EasyPusher手机直播推送是如何实现后台直播推送的
    EasyPusher手机直播推送是如何实现后台直播推送的
    EasyDarwin开源流媒体服务器支持basic基本认证和digest摘要认证解析
  • 原文地址:https://www.cnblogs.com/geloutingyu/p/8350155.html
Copyright © 2011-2022 走看看