前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >C++11 你真的会用迭代器(iterator)么?

C++11 你真的会用迭代器(iterator)么?

作者头像
10km
发布2022-05-07 10:08:05
3940
发布2022-05-07 10:08:05
举报
文章被收录于专栏:10km的专栏

C++ STL提供了丰富的标准容器(Container)对象(vector,array,queue,list,set,unordered_map/set…),让我们可以根据需求选择不同的容器管理各种类型的数据。说到使用容器,不用迭代器(iterator)是不可能的,所有的容器对象都根据容器的特点都提供了类似但不同的iterator,用于访问容器中的数据。

迭代器(iterator)循环

一般来说,如果要遍历一个容器中的所有数据,程序员们最常用的写法是:

代码语言:javascript
复制
#include <list>
#include <iostream>
int main(){
list<int> lst;
for(list<int>::iterator itor=lst.begin();itor!=lst.end();itor++){
    cout<<(*itor)<<endl;
    //do something
}
}

基于范围的for循环

C++11提供了关于for循环的新特性:基于范围的for循环( the range-base for statement),再加上”类型推导”特性可以将上面的代码进一步简化:

代码语言:javascript
复制
for(auto &node:lst){
    cout<<node<<endl;
    //do something
}

没有区别吗?

显然,新的for循环写法更简洁,但新的for循环写法的优点仅此而已吗? 仔细琢磨,你会注意到,第一种写法,每次循环,都要调用lst.end(),

这是list.end()函数的源代码(来自C++11中头文件stl_list.h):

代码语言:javascript
复制
      /**
       *  Returns a read/write iterator that points one past the last
       *  element in the %list.  Iteration is done in ordinary element
       *  order.
       */
      iterator
      end() _GLIBCXX_NOEXCEPT
      { return iterator(&this->_M_impl._M_node); }

可以看出,每一次调用end()函数,都会返回一个iterator对象,根据迭代器的特性我们可以知道在整个迭代循环过程中,每次调用end()返回的对象其实都是完全一样的,而每次的调用都不可避免会发生对象构造、复制。。。等等动作,这对于要求高性能的应用场合,这种无意义的重复是不可接受的。

那么基于范围的for循环( the range-base for statement)会不会是同样的处理方式呢? 为了验证这个问题,我做了一个试验: 在我的上一篇文章 《C++11 为自定义容器实现标准的forward迭代器》中我实现了一个基于自定义哈希表(HashTableAbstract)的标准forward迭代器。于是我在HashTableAbstract 的end()函数中加入了调试代码,这样每次end()都会输出调试信息:

代码语言:javascript
复制
    iterator end()noexcept
    //{ return iterator(this->m_table,this->m_table.capacity); }//原代码
    {
        cout << "return a end iterator" << endl;//输出调试信息
        return iterator(this->m_table, this->m_table.capacity);
    }

然后运行如下测试代码:

代码语言:javascript
复制
    HashSetCl<int> testhash;//HashSetCl是基于HashTableAbstract子类,实现哈希集合
    testhash.insert(2000);//加入3条数据
    testhash.insert(65535);
    testhash.insert(12345);
    cout<<"testing for statement using iterator:"<<endl;//使用迭代器循环
    for (auto itor = testhash.begin(); itor != testhash.end(); itor++) {
        cout << *itor << endl;
    }
    cout<<"testing for the range-base for statement:"<<endl;//使用基于范围的for循环
    for (auto n : testhash) {
        cout << n << endl;
    }

以下是程序输出(debug/release结果一样)

testing for statement using iterator://注,循环中调用了三次end() return a end iterator 2000 return a end iterator 12345 return a end iterator 65535 return a end iterator testing for the range-base for statement://注,循环中调用了一次end() return a end iterator 2000 12345 65535

总结

上面的输出可以证实,基于范围的for循环( the range-base for statement)只会在循环开始的时候调用一次end(),与一般直接使用迭代器(iterator)的循环相比,不仅具备代码更简洁的优点,性能上也更具备优势。当然这个结论只在无序容器迭代遍历(只读)的情况下才有效(无序容器只提供forward迭代器),具备随机访问迭代器(random-access iterator)的容器(比如 vector,array),直接用下标访问才是最快的。

如果你还是”坚持传统”,习惯直接使用迭代器来工作,那么建议对代码做一些改进,还以最前面的代码为例,在循环开始时调用一次end()函数保存成临时变量end,然后每次循环比较的时候不再调用end()函数,而是直接与临时变量end相比,就避免了重复调用end()

代码语言:javascript
复制
for(auto itor=lst.begin(),end=lst.end();itor!=end;itor++){
    cout<<(*itor)<<endl;
    //do something
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2015-11-22,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 迭代器(iterator)循环
  • 基于范围的for循环
  • 没有区别吗?
  • 总结
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档