2

我需要堆栈溢出的意见来解决我脑海中关于循环效率的问题。现在,当我开始编程时,“让它工作”是一个重中之重,我的大多数 for 循环看起来都是这样的。

for (int i = 0; i < N; i++) {;}

然后我很紧张,尤其是在 C++ 开发中,预增量可以为您节省一些调用,并且考虑到这将是 N 个调用并且它不会改变我习惯的可读性。

for (int i = 0; i < N; ++i) {;}

这在一段时间内已经足够好了,但专注于可读性,在阅读了 Steve McConnell 的一些 Code Complete 之后,我明白了这一点。

for (int loop_index = 0; loop_index < loop_count; ++loop_index) {;}

这些变量会根据代码的上下文而变化。然后我阅读了一些关于内置类型构造函数和赋值的 Effective C++。基本上这之间的区别

int i = 42;

int i(42); 

是不是前者调用了构造函数和赋值运算符,而后者只调用了构造函数。所以我在编码时把它纳入了我的日常工作。所以我的问题是,这是编写 for 循环的最有效和可读的方式:

for (int loop_index(0); loop_index < loop_counter; ++loop_index) {;}
4

6 回答 6

5

实际上,

for (int i = 0; i < N; i++) {;}

很好。i是一个int,所以编译器无论如何都会优化后增量。是否初始化intwithint i(42);或 withint i = 42;也是一个品味问题。

我也会调用迭代器i,而不是loop_index. 前者是普遍理解的,而后者是陌生的。

但是,如果您正在处理迭代器,则情况会发生变化。机会it++仍然会优化到++it,但在这里,我宁愿使用预增量。

于 2012-08-08T13:35:49.753 回答
2

如果您不使用迭代器,那没关系,因为编译器会将其转换为相同的代码。

附录:如果您使用迭代器:

for(vector<int>::iterator i = a.begin(); i != a.end(); ++i)

然后 ++i 是 i.operator++() 而 i++ 是 i.operator++(int) 这可能意味着两个不同的东西。

顺便提一句。如果这是一个问题,您可以衡量性能。

于 2012-08-08T13:35:35.377 回答
2

首先,你的最后一点是不正确的。这条线

int i = 42;

确实复制初始化(参见这个相关的 GoTW)。那里没有任务。对于分配,你需要

int i;
i = 42;

其次,在处理整数类型等内置函数时,编译器生成的代码可能没有区别。您应该分析不同的选项并查看。

于 2012-08-08T13:35:40.590 回答
1

如果您使用内置类型或大多数迭代器,所有这些都将编译为相同的代码。

所以我会坚持使用最适合 C++ 的那个。

for (int i = 0; i < N; ++i) {;}

或者如果你想罗嗦(但 i 和 k 传统上是循环索引)

for (int loop_index = 0; loop_index < loop_count; ++loop_index) {;}

作为我最初主张的证据:这两段代码生成相同的汇编代码。(使用 OS X 编译的 g++ 4.0.1 g++ -Os -S test.cpp

void f(int); // So that smart compilers can't optimise away the loop entirely.

void g()
{
  int N=100;
  // for( int i=0; i<N; i++)
  for (int i(0); i < N; ++i) 
  {
    f(i);
  }
}

为了完整起见,生成的代码是:

.globl __Z1gv
__Z1gv:
LFB2:
    pushl   %ebp
LCFI0:
    movl    %esp, %ebp
LCFI1:
    pushl   %esi
LCFI2:
    xorl    %esi, %esi
    subl    $20, %esp
LCFI3:
L2:
    movl    %esi, (%esp)
    incl    %esi
    call    L__Z1fi$stub
    cmpl    $100, %esi
    jne L2
    addl    $20, %esp
    popl    %esi
    leave
    ret
于 2012-08-08T13:34:43.520 回答
0

虽然其他人已经很好地涵盖了 post-vs pre-increments 和初始化,但我想补充一点,int i在大多数情况下 using 比 更具可读性int loop_index,因为这是其他人都在使用和期望的。

您可能想要将其称为其他名称的唯一情况是当您有多个循环以及循环索引之间的复杂交互时。例如

for (int row = 0; row < rows; row++) {
    for (int column = 0; column < columns; column++) {
        stuff[row * columns + column] = calc_stuff(row, column);
    }
}
于 2012-08-08T13:44:12.713 回答
0

高效的?不,它很可能是相同的,因为大多数编译器会优化 int 和简单指针的后增量和初始化。至于 loop_index 是否比 i 更具可读性,IMO,对于简单的计数器来说,它的可读性较差。

如果它是一个迭代器,那就不同了,编译器不能总是优化迭代器的重载后增量或双重初始化。我也更喜欢将迭代对象的名称用于迭代器,因为这反映了更好的使用方式,当使用箭头 -> 运算符取消引用迭代器时,使用对象的名称看起来更自然。

于 2012-08-08T13:52:55.307 回答