【问题标题】:Best way to iterate through a container遍历容器的最佳方法
【发布时间】:2010-10-04 15:35:28
【问题描述】:

这两种迭代容器的方法有哪些优点/缺点/您更喜欢哪一种以及为什么:

for (MyClass::iterator i = m.begin(), e = m.end() ; i != e ; i++)
{
    // ...
}

for (MyClass::iterator i = m.begin() ; i != m.end() ; i++)
{
    // ...
}

辅助问题:i++ 还是 ++i?为什么?

【问题讨论】:

    标签: c++ iterator containers


    【解决方案1】:

    除非您关闭了优化,否则两者都是等效的。至于 i++ 或 ++i,++i 效率更高,因为它不涉及临时值。

    【讨论】:

      【解决方案2】:

      实际上我总是做第二个,虽然我有时会担心如果多次调用 end 会减慢速度。我的印象是这会被优化,但不确定。

      而且 ++i 绝对是。它永远不会比 i++ 慢,如果有的话,它会更快。

      【讨论】:

        【解决方案3】:

        第一个更快,因为不是每次迭代都调用end()。不,优化器不能轻易地为你缓存它,因为它不知道容器的大小在这次迭代中是否发生了变化(因此最终移动了)。由于别名问题,这也适用于 const 容器。

        i++ 返回 i 的副本,然后递增。 ++i 递增,然后返回递增的值。因此,当您丢弃返回值时,请使用++i,因为它需要做的工作更少(无需复制)。优化器很可能会修复内联的 i++ 调用,因此它与 ++i 一样快,但不要依赖于此。

        我?我用

        for(int i = 0; i < m.size(); i++) {
            // ... do something with m[i]
        }
        

        因为它是最短且最清晰的。为什么是int 而不是MyClass::size_type?因为它更简单,而且到目前为止我从来不用担心边缘情况。为什么i++?因为对于基本类型,它总是被优化为++i,并且它不会让同事感到困惑。作为奖励,我也可以使用i 作为数值。使用迭代器,我必须保留一个单独的计数器,或者使用 std::distance

        obecalp 指出这不适用于一半的标准容器,例如listmap。事实上,那些需要使用适当的迭代器。与此相关的是,在编写通用代码时,您应该始终使用iterator

        【讨论】:

        • 你假设它是一个支持 operator[]、map/set、list 和许多其他不支持 operator[] 的容器。
        • 啊,是的!有一次我迭代地图时,我使用了 map::iterator。到目前为止,我从未使用过列表,但我也会使用迭代器。
        • 您应该使用 size_t 而不是 int。 混合有符号和无符号类型是意外添加无限循环边缘情况的好方法。
        • Tom:如果不是 unsigned 由于提升规则而倾向于默默地搞砸计算,那是有道理的。考虑“无符号整数 i = 6”。在你的头脑中计算“i / -2”、“i % -2”和“i * -2”。现在将它们打入编译器。你得到你所期望的了吗? :) ...我从不使用无符号的。
        • 为什么要对迭代器索引进行乘法运算?
        【解决方案4】:

        对于普通的 stl 迭代器并没有太大的区别,但是如果你的集合很复杂并且要求 end 很昂贵,那么只要求 end 一次可能会更快。

        对于 ++i 与 i++ 类似,当迭代器是一个复杂的类(而不仅仅是 stl 迭代器中的指针)时,++i 可能是一个更昂贵的操作返回之前状态的迭代器副本。对于 ++i,它返回当前状态的迭代器,因此可以返回对自身的引用。

        通常最好只在您的分析器发现存在问题时才优化您的代码 - 最好让代码尽可能易于阅读。

        【讨论】:

        • 我想知道是否有一个词是“在整个代码中分散小的减速,因此它们会累积并显着影响性能,但无法被分析器检测到”。
        【解决方案5】:

        如果迭代器是非平凡的(即不是指针),++i 肯定更快,因为它不涉及到临时的副本,可能会或可能不会优化。

        第一种形式稍快一些,但如果您在循环中删除或插入内容,则可能会出错。

        对于我使用的容器的简单迭代

        #define foreach BOOST_FOREACH // in some header
        
        foreach(MyType &element, any_container) {
          // deal with element
        }
        

        大部分时间都是为了简洁明了。

        【讨论】:

        • 酷,我不知道 BOOST_FOREACH。我在 boost 中发现了越来越多有用的东西。也将开始使用这个。 :)
        【解决方案6】:

        Boost.Foreach 介绍了一个不错的方法:

        #define foreach         BOOST_FOREACH
        // ...
        Container<Item> container;
        // ...
        foreach (Item item, container) {
          // do some stuff with the item
        }
        

        【讨论】:

          【解决方案7】:

          C++“容器中的每个元素”循环在上下文不需要迭代逻辑的情况下是最有效的。

          for(Item& i : Container)
          {
               dosomething(i);
          }
          

          for(const Item& i : Container)
          {
               dosomething(i);
          }
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2012-05-16
            • 2018-03-09
            • 1970-01-01
            • 2018-11-01
            • 2012-07-16
            • 2011-04-24
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多