在C++中,for循环中的条件调用

3

我一直在想一个简单的问题:

当我进行如下循环时:

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

每次循环都要检查条件 i < myVector.size(),我应该在循环之前将数组的大小存储在变量中,以避免每次迭代调用 size() 函数吗?还是编译器足够聪明,可以自己处理?

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

我想把问题扩展到更复杂的条件,例如 i < myVector.front()/myVector.size()

编辑:我在循环中没有使用myVector,它只是用来给出结束条件。那么更复杂的条件怎么办?


谢谢大家的回答!但是没有人回答更复杂的条件...编译器是否足够聪明,能看到在循环内部不修改向量的情况下,除法的结果不会改变? - Arcyno
1
我不知道编译器是否聪明到能够注意到 myVector.front() / myVector.size() 是一个常量表达式(假设你在循环内部不修改向量)。无论如何,这是一个优化问题,结果可能会因编译器版本和优化模式而异。因此,我不会告诉你是否应该在循环之前计算终止值;只需将代码编译为汇编源代码,看看编译器是否在每次迭代中产生完整的计算即可。如果是这样,那么自己进行优化可能是有意义的。 - CiaPan
5个回答

2
答案主要取决于您的循环内容 - 它可能会在处理过程中修改向量,从而修改其大小。
但是,如果只是扫描向量,则可以安全地提前存储其大小:
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变量,因此预取长度不会带来任何收益。

1
我会说,
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是一个常量,不会在循环中改变。 - cerkiewny
它只是使我的大小不改变,数组仍然可以改变大小。 - David Haim
2
虽然我会说在循环中修改向量总是一个坏主意,除非你完全理解自己在做什么。例如,如果你在向量的中间插入一个元素,你仍然可能会丢失一些元素。更糟糕的是,在同一个循环的不同样式中,比如使用iteratorbegin()/end(),修改向量可能会导致更大的问题。 - Petr
是的,但它明确表示迭代计数在开始时固定且不应更改,如果有人尝试更改它,则会违反const修改规则。在某些算法中,您正在推送向量末尾并且不希望遍历新元素...这实际上取决于用例。 - cerkiewny
迭代数组并在元素不满足条件时删除它是一种常见的做法。无论是好还是坏的做法,这都是非常普遍的。 - David Haim

1
如果在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举例。 - medevil

0

任何聪明的编译器都可能会将其优化掉。但是为了确保,我通常会像这样布置我的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)
{

}

0

开销非常小。 vector.size() 不重新计算任何内容,只是返回私有大小变量的值。

与预缓冲该值相比,它更安全,因为当元素从向量中弹出或推入/从向量中推出时,向量的内部大小变量会更改。

如果编译器能够预测在 for 循环运行时向量不会被任何东西更改,则可以将其优化掉。 如果其中存在线程,则很难完成此操作。

但是,如果没有使用任何线程,则很容易进行优化。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接