【问题标题】:call to condition on for loop (c++)调用 for 循环的条件 (c++)
【发布时间】:2015-07-23 01:43:23
【问题描述】:

这是一个我想了很久的简单问题: 当我做一个这样的循环时:

for (int i = 0; i < myVector.size() ; ++i) {
    // my loop
}

由于每次都会检查条件i &lt; myVector.size(),我是否应该在循环之前将数组的大小存储在变量中以防止每次迭代调用 size() ?还是编译器足够聪明,可以自己做?

mySize = myVector.size();   
for (int i = 0; i < mySize ; ++i) {
    // my loop
}

我会用一个更复杂的条件来扩展这个问题,例如i &lt; myVector.front()/myVector.size()

编辑: 我不在循环内使用 myVector,只是在这里给出结束条件。那么更复杂的情况呢?

【问题讨论】:

  • 感谢大家的回答!但是没有人回答更复杂的条件。编译器是否足够聪明,可以看到除法的结果不会因为我不修改循环内的向量而改变?
  • 我不知道是否(或:哪个)编译器足够聪明,可以注意到 myVector.front()/myVector.size() 是一个常量表达式(前提是您不修改循环内的向量)。无论如何,这是一个优化问题,编译器版本和优化模式之间的结果可能会有所不同。所以我不会告诉你是否应该在循环之前计算终止值;只需将您的代码编译为汇编源代码,然后查看编译器是否会在每次迭代中产生完整的计算。如果是这样,那么自己优化可能是有意义的。

标签: c++ performance for-loop


【解决方案1】:

答案主要取决于循环的内容——它可能会在处理过程中修改向量,从而修改其大小。

但是,如果刚刚扫描矢量,您可以安全地提前存储其大小:

for (int i = 0, mySize = myVector.size(); i < mySize ; ++i) {
    // my loop
}

虽然在大多数类中,像“获取当前大小”这样的函数只是内联 getter:

class XXX
{
public:
    int size() const { return mSize; }
    ....
private:
    int mSize;
    ....
};

因此编译器可以轻松地将调用减少为仅读取 int 变量,因此预取长度不会带来任何好处。

【讨论】:

    【解决方案2】:

    如果您在 for 循环期间没有更改向量中的任何内容(添加/删除)(这是正常情况),我会使用 foreach 循环

    for (auto object : myVector)
    {
      //here some code
    }
    

    或者如果你不能使用 c++11 我会使用迭代器

    for (auto it = myVector.begin(); it != myVector.end(); ++it)
    {
      //here some code
    }
    

    【讨论】:

    • 但是为什么要使用迭代而不是 foreach 或迭代器呢?作者没有提到他不能使用 foreach 例如
    【解决方案3】:

    我会这么说

    for (int i = 0; i < myVector.size() ; ++i) {
        // my loop
    }
    

    安全一点
    mySize = myVector.size();   
    for (int i = 0; i < mySize ; ++i) {
        // my loop
    }
    

    因为myVector.size() 的值可能会发生变化(由于 ,例如循环内的push_back(value)),因此您可能会错过一些元素。
    如果您 100% 确定 myVector.size() 的值不会改变,那么两者是一回事。
    然而,第一个比第二个更灵活(其他开发人员可能不知道循环迭代固定大小,他可能会改变数组大小)。别担心编译器,他比我们俩加起来都聪明。

    【讨论】:

    • 甚至更好的可读性可以用 const mySize = myVector.size(); - 你可以清楚地看到 mySize 是恒定的,不会在循环中改变。
    • 它只是让mySize不改变,数组仍然可以改变大小。
    • 虽然我会说在循环中修改向量是一个坏主意,至少除非你完全理解你在做什么。例如,如果你在向量的中间插入一个元素,你仍然可能会丢失一些元素。更糟糕的是,在同一循环的不同样式中,例如 iteratorbegin()/end() 修改向量可能会导致更大的问题。
    • 是的,但它明确表示迭代计数在开始时是固定的,不应更改,如果有人试图更改它,它将违反 const 修改,在某些算法中,您正在推动向量结束并且不想遍历新元素......这真的取决于用例。
    • 一种常见的做法是遍历数组并在不满足条件时删除元素。不好或好的做法,这很常见..
    【解决方案4】:

    开销非常小。 vector.size() 不会重新计算任何东西,只是简单地返回私有大小变量的值..

    它比预先缓冲值更安全,因为当元素被弹出或推送到向量/从向量中推送时,向量内部大小变量会发生变化。..

    可以编写编译器来优化这一点,当且仅当它可以预测在 for 循环运行时向量不会被任何更改。 如果里面有线程,那就很难做到了。

    但如果没有任何线程在进行,优化它非常容易。

    【讨论】:

      【解决方案5】:

      任何智能编译器都可能对此进行优化。然而,为了确保我通常这样布置我的 for 循环:

      for (int i = myvector.size() -1; i >= 0; --i)
      {
      
      }
      

      有几点不同:

      • 迭代以相反的方式完成。尽管在大多数情况下这应该不是问题。如果是我更喜欢 David Haim 的方法。

      • 使用--i 而不是i--。理论上 --i 更快,尽管在大多数编译器上它不会产生影响。

      如果你不关心这个索引:

      for (int i = myvector.size(); i > 0; --i)
      {
      
      }
      

      也是一种选择。总的来说,我不使用它,因为它比第一个更令人困惑。并且不会为您带来任何性能。

      对于像std::vectorstd::list 这样的类型,迭代器是首选方法:

      for (std::vector</*vectortype here*/>::iterator i = myVector.begin(); i != myVector.end(); ++i)
      {
      
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-12-17
        • 1970-01-01
        • 1970-01-01
        • 2013-05-27
        • 1970-01-01
        • 2016-04-10
        • 2016-08-03
        • 2021-05-06
        相关资源
        最近更新 更多