如果你要让某个变量的值加1,可以用 i++; 也可以用++i;
通常,编码规范都建议我们如果考虑性能,这种情况下最好用前置自增,因为后置自增会多一次无用的复制操作。
但这种做法往往跟我们的习惯不相符,我自己还是看“i++”顺眼一点儿。
也许,编译器没那么傻,它们可以根据使用环境优化后置自增,真是这样的话,我们就不必破坏自己的习惯了?
试验一下吧。原始类型的自增操作肯定是可以能被编译器优化的。那么自己写的类的后置自增操作符能否被优化呢?
我在VC2008(vc9)里写了下面的代码:
class t_iter
{
public:
t_iter(void):m_n(0){}
~t_iter(void){}
public:
t_iter & operator ++ (){
m_n++;
return *this;
}
t_iter operator ++ (int){
t_iter tempIter = *this;
m_n++;
return tempIter;
}
int GetValue() const {
return m_n;
}
private:
int m_n;
};
int _tmain(int argc, _TCHAR* argv[])
{
t_iter iter;
iter++;
return iter.GetValue();
}
代码里面11~15行的“t_iter operator ++ (int)”这个函数就是后置自增操了。t_iter & operator ++ ()是前置操作。
我选了Maximize Speed (/O2)优化选项,也开着“Use Link Time Code Generation (/ltcg)”(其实不开这个也可以的,因为我把t_iter的代码和调用它的main函数写在同一个.cpp文件里了。
打开汇编文件输出选项,并选择Assembly With Source Code (/FAs),build整个程序,就可以看到我们期待的答案了。
编译后产生的汇编文件中main函数的部分如下:
PUBLIC _wmain
; Function compile flags: /Ogtpy
; COMDAT _wmain
_TEXT SEGMENT
_iter$ = -8 ; size = 4
$T12223 = -4 ; size = 4
_argc$ = 8 ; size = 4
_argv$ = 12 ; size = 4
_wmain PROC ; COMDAT
; 40 : {
sub esp, 8
; 41 :
; 42 : t_iter iter;
; 44 : iter++;
lea eax, DWORD PTR $T12223[esp+8] ;准备存放后置自增函数的返回值
lea edx, DWORD PTR _iter$[esp+8]
mov DWORD PTR _iter$[esp+8], 0
call ??Et_iter@@QAE?AV0@H@Z ; t_iter::operator++ 调用后置自增函数
; 48 : return iter.GetValue();
mov eax, DWORD PTR _iter$[esp+8]
add esp, 8
ret 0
_wmain ENDP
_TEXT ENDS
END
看第22行,编译器并没有把t_iter的后置自增函数内嵌到main函数中,而是在main函数中调用了后置自增函数,那么就不可能对这个进行优化了。
汇编文件中后置自增函数的部分如下:
; COMDAT ??Et_iter@@QAE?AV0@H@Z
_TEXT SEGMENT
??Et_iter@@QAE?AV0@H@Z PROC ; t_iter::operator++, COMDAT
; _this$ = edx
; ___$ReturnUdt$ = eax
; 20 :
; 21 : t_iter tempIter = *this;
mov ecx, DWORD PTR [edx]
mov DWORD PTR [eax], ecx ;准备好返回值,这条指令显然在单纯的自增调用中是无用的
; 22 : m_n++;
inc ecx
mov DWORD PTR [edx], ecx
; 23 : return tempIter;
; 24 : }
ret 0
??Et_iter@@QAE?AV0@H@Z ENDP ; t_iter::operator++
_TEXT ENDS
我感到奇怪的是,编译器并没有把这个后置自增函数内嵌到调用函数中。我还试了在函数声明前加上__inline和 __forceinline,编译器依然无动于衷,始终不真的inline这个函数。我把operator ++ (int)改成一个普通函数,仍然无济于事。
无奈,我们使用自增函数的时候还是应该养成使用前置自增的习惯。当然,如果我们始终能分清我们要自增的变量是原始类型变量还是对象,那么我们可以选择在简单变量上按照顺眼的标准继续使用 i++,而在对象上使用 ++iterator;