C++迭代器

迭代器

标准库容器类型上所有迭代器都允许我们访问容器中的元素,下面的表中列出了容器迭代器支持的所有操作,其中有一个例外:forward_list迭代器不支持递减运算符。

迭代器操作 说明
*iter 返回迭代器所指元素的引用
iter->member 解引用iter并获取该元素的名为member的成员,等价于(*iter).member
++iter 令iter指向容器中下一个元素
--iter 令iter指向容器中上一个元素
iter1 == iter2 判断两个迭代器是否指向的是同一个元素或者是同一个容器的尾后迭代器
iter1 != iter2 同上

容器中的begin和end迭代器指示了容器中的元素范围,其中end迭代器不指向最后一个元素,而是指向尾元素之后的位置,这种元素范围被称为左闭合区间:[begin,end),假设begin和end构成了一个合法的迭代器范围,则:

  • 如果begin与end相等,则范围为空。
  • 如果begin与end不等,则范围至少包含一个元素,且begin指向该范围中的第一个元素。
  • 我们可以对begin递增若干次使得begin==end。

begin和end有多个版本,其中带r的版本返回反向迭代器,c开头的版本返回const迭代器。

使用迭代器遍历数组:

int main()
{
    int values1[] = { 1,2,3,4,5 };
    for (auto it = begin(values1); it != end(values1); it++) {
        cout << *it << endl;
    }
    system("pause");
}

插入迭代器

插入迭代器是一种迭代器适配器,它接受一个容器,生成一个迭代器,能实现向给定容器添加元素,当我们给一个插入迭代器赋值时,迭代器调用容器操作来向给定容器的指定位置插入一个元素,插入完成后迭代器保持不变。*it,++it,it++不会对插入迭代器做任何事情,每个操作都返回it。

插入迭代器有三种类型:

  • back_inserter,创建一个使用push_back的迭代器。
  • front_inserter,创建一个使用push_front的迭代器。
  • inserter创建一个使用insert的迭代器,此函数接受第二个参数,这个参数必须是一个指向给定容器的迭代器,元素将被插入到给定迭代器所表示的元素之前。
int main()
{
    vector<int> values{ 1,2,3,4 };
    list<int> list1, list2,list3;
    copy(values.cbegin(), values.cend(), back_inserter(list1));
    copy(values.cbegin(), values.cend(), front_inserter(list2));
    copy(values.cbegin(), values.cend(), inserter(list3,list3.begin()));
    for (const auto& e : list1) {
        cout << e << endl;//1 2 3 4
    }
    for (const auto& e : list2) {
        cout << e << endl;//4 3 2 1
    }
    for (const auto& e : list3) {
        cout << e << endl;//1 2 3 4
    }
    system("pause");
}

iostream迭代器

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

创建一个流迭代器时,必须指定迭代器将要读写的对象类型,还可以将流迭代器绑定到一个流,如果不绑定它将是一个可以当作尾后值使用的流迭代器。一旦关联的流遇到文件尾或遇到IO错误,迭代器的值就与尾后迭代器相等。

int main()
{
    vector<int> values;
    istream_iterator<int> inIter(cin);
    istream_iterator<int> eof;
    while (inIter != eof) {
        //先调用后置递增运算符,然后对迭代器的旧值进行解引用
        values.push_back(*inIter++);
    }
    for (const auto& e : values) {
        cout<<e<<endl;
    }
    system("pause");
}

我们可以将上面的代码重写为如下形式,values构造时从cin读取数据,直到遇到文件尾或者不是int的数据为止。

int main()
{
    istream_iterator<int> inIter(cin), eof;
    vector<int> values(inIter, eof);
    for (const auto& e : values) {
        cout<<e<<endl;
    }
    system("pause");
}

由于算法使用迭代器操作来处理数据,而流迭代器又至少支持某些迭代器操作,因此我们可以使用某些算法来操作流迭代器,下面的代码计算从输入流读取的值的和。

int main()
{
    istream_iterator<int> inIter(cin), eof;
    cout << accumulate(inIter, eof, 0) << endl;
    system("pause");
}

当我们将一个istream_iterator绑定到一个流时,标准库并不保证迭代器立即从流读取数据,具体实现可以推迟从流中读取数据,标准库保证的是在我们解引用迭代器之前,从流中读取的操作已经完成了。

int main()
{
    vector<int> values;
    istream_iterator<int> inIter(cin);
    //输入一个值后才会显示ready now
    cout << "ready now" << endl;
    values.push_back(*inIter);
    system("pause");
}

当创建一个ostream_iterator时,我们可以提供(可选的)第二个参数,它是一个C风格字符串,在输出每个元素后都会打印此字符串。

ostream_iterator必须绑定到一个流,*out,++out,out++这些运算符虽然存在但不对out做任何事情,每个运算符都返回out。

对ostream_iterator使用赋值运算符(=)时,会用<<运算符将等号右边的值写入到所绑定的流中,该值的类型必须与流可写的类型兼容。

int main()
{
    ostream_iterator<int> outIter(cout," ");
    vector<double> values{1.0,2.0,3.0};
    for (const auto& e : values) {
        outIter = e;//1 2 3
    }
    system("pause");
}

还可以通过copy打印vector中的元素,这比上面的代码更简单。

int main()
{
    ostream_iterator<int> outIter(cout," ");
    vector<int> values{1,2,3};
    copy(values.cbegin(), values.cend(), outIter);//1 2 3
    system("pause");
}

反向迭代器

反向迭代器就是在容器中从尾元素向首元素反向移动的迭代器,对于反向迭代器,递增或递减的操作含义会颠倒过来。通过成员函数base可以获得反向迭代器对应的普通迭代器。

int main()
{
    vector<int> values1 = { 10,2,8,0,9,1 };
    //按逆序排序
    sort(values1.rbegin(),values1.rend());
    ostream_iterator<int> outIter(cout," ");
    copy(values1.cbegin(), values1.cend(), outIter);//10 9 8 2 1 0
    //反向输出
    copy(values1.crbegin(), values1.crend(), outIter);//0 1 2 8 9 10
    //通过base()正向输出
    copy(values1.crend().base(), values1.crbegin().base(), outIter);//10 9 8 2 1 0
    system("pause");
}

移动迭代器

移动迭代器通过改变给定迭代器的解引用运算符的行为来适配此迭代器,普通迭代器的解引用运算符返回一个指向元素的左值,而移动迭代器解引用运算符生成一个右值引用。我们通过调用make_move_iterator将普通迭代器转换为一个移动迭代器。

由于移动一个对象可能销毁原对象,因此只有在确信算法或自定义的函数使用完元素后不再访问它时,才能将移动迭代器传递过去。

在库代码中使用移动操作(std::move,make_move_iterator)能提升性能,但是如果在用户代码中随意使用移动操作,很可能会导致莫名其妙,难以查找的错误。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,406评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,732评论 3 393
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,711评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,380评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,432评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,301评论 1 301
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,145评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,008评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,443评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,649评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,795评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,501评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,119评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,731评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,865评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,899评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,724评论 2 354

推荐阅读更多精彩内容

  • 迭代器 (iterator) 是 C++ 程序中常用的一种设计模式,它最重要的作用是为访问容器提供了统一的接口。 ...
    番茄吐司君阅读 13,499评论 3 9
  • 前言 把《C++ Primer》[https://book.douban.com/subject/25708312...
    尤汐Yogy阅读 9,516评论 1 51
  • 2. C++标准库 2.1 IO库 IO对象无拷贝或赋值,进行IO操作的函数通常以引用方式传递和返回流。 IO库条...
    王侦阅读 1,320评论 0 0
  • 迭代器 1972年,麻省理工学院的Barbara Liskov和一组科学家开始研究新的编程方法,他们将重点放在对用...
    JunChow520阅读 2,069评论 0 1
  • 这周的课讲了将泛型算法。现在将泛型算法收集下,备用。 (1)泛型算法用迭代器来解决第一个要求:遍历容器。所有迭代器...
    shenhua8369阅读 246评论 0 0