2012-06-15 32 views
4

有时,在查看优化代码时,我发现仅在循环范围内使用的参数将其声明移至循环之外。在循环范围外声明参数会更有效吗?

事情是这样的:

A arr[BIG_NUMBER]; 

// .... // 

for (int i=0; i!=BIG_NUMBER; ++i) 
{ 
    B b = arr[i].getB(); 
    // ... do some work with b. 
} 

正在变成这样:

A arr[BIG_NUMBER]; 

// .... // 

B b; 
for (int i=0; i!=BIG_NUMBER; ++i) 
{ 
    b = arr[i].getB(); 
    // ... do some work with b. 
} 

推测的理由是,我们节省了不断重新声明b。但这是一个合理的事情吗?而且,答案根据B是原始类型还是类而不同?

我原以为虽然for循环内变量的作用域限制可能会阻止它们在循环外被访问,因为循环的内容存在于同一个栈帧中,所以'actual'声明只发生一次。

(NB,我已经考虑Creating an object in the loop但考虑到这是一个不同的问题,因为它是与声明,而不是初始化相关的任何费用。)

编辑 - 改进的标题

+1

@JoachimPileborg它的确很重要,如果构造/析构函数是昂贵的。 –

+0

编译器也可以选择调整块进入/退出时的堆栈指针(不记得是否可以让g ++来做到这一点,默认情况下它不会),虽然成本相对于循环构造。 –

+0

注意:额外的范围可以通过将所有东西包裹在另一对大括号中来抵消。 '{B b; for(...){}}'是一个可接受的构造,通常用于RAII构造。 –

回答

8

如果它是一个原始类型,编译器会相应地进行优化。

如果它是用户定义的类,则取决于它。有什么更昂贵的,一个额外的初始化或BIG_NUMBER析构?

比较这些:

B b; //one initialization 
for (int i=0; i!=BIG_NUMBER; ++i) 
{ 
    b = arr[i].getB(); //BIG_NUMBER assignments 
} 

for (int i=0; i!=BIG_NUMBER; ++i) 
{ 
    B b = arr[i].getB(); //BIG_NUMBER initializations 
          //should be the same as an assignment 
} //BIG_NUMBER objects destroyed 
+1

而当它产生变化的时候,当对象拥有一些昂贵的构造资源时,它不受赋值的影响(所以你应该在循环之外初始化)。或者,当'getB'按值返回时,你会得到一个复制elision,当然这对于赋值是不可能的(所以你应该在循环内初始化,或者交换而不是赋值)。移动语义主要减轻后者。 –

1

有没有一般的答案。这取决于B的类型,您正在使用的编译器 ,以及您可能在循环中(在 分配之后)做了什么。你所能做的只是测量,甚至只会告诉你关于一个特定的编译器,在一台特定的机器上运行。