建议12:优先使用前缀操作符
也许从开始接触C/C++(www.cppentry.com)程序的那天起,就记住了前缀和后缀运算,知道了++的前缀形式是“先加再用”,后缀形式是“先用再加”。前缀和后缀运算是C和C++(www.cppentry.com)语言中的基本运算,它们具有类似的功能,区别也很细微,主要体现在运行效率上。
分析下面的代码片段:
- int n=0, m=0;
- n = ++m; /*m先加1, 之后赋给n*/
- cout << n << m; /*结果:1 1*/
在这个例子中,赋值之后,n等于1,因为它是在将m赋予n之前完成的自增操作。再看下面的代码:
- int n=0, m=0;
- n = m++; /*先将m赋于n, 之后m加1*/
- cout << n << m; /*结果:0 1*/
这个例子中,赋值之后,n等于0,因为它是先将m赋予n,之后m再加1的。
为了更好地理解前缀操作符和后缀操作符之间的区别,可以查看这些操作的反汇编代码。即使不了解汇编语言,也可以很清楚地看到二者之间的区别,注意inc指令出现的位置:
- /* m=n++;的反汇编代码*/
- mov ecx, [ebp-0x04] /*store n's value in ecx register*/
- mov [ebp-0x08], ecx /*assign value in ecx to m*/
- inc dword ptr [ebp-0x04] /*increment n*/
- /*m=++n;的反汇编代码*/
- inc dword ptr [ebp-0x04] /*increment n;*/
- mov eax, [ebp-0x04] /*store n's value in eax register*/
- mov [ebp-0x08], eax /*assign value in eax to m*/
从汇编代码可以看出,两者采取了相同的操作,只是顺序稍有不同而已。但是,前缀操作符的效率要优于后缀操作符,这是因为在运行操作符之前编译器需要建立一个临时的对象,而这还要从函数重载说起。
重载函数间的区别取决于它们在参数类型上
- ClassName & ClassName::operator++()
- {
- ClassAdd (1); //increment current object
- return *this; //return by reference the current object
- }
-
- ClassName ClassName::operator++(int unused)
- {
- ClassName temp(*this); //copy of the current object
- ClassAdd (1); //increment current object
- return temp; //return copy
- }
的差异,但不论是自增的前缀还是后缀,都只有一个参数。为了解决这个语言问题,C++(www.cppentry.com)规定后缀形式有一个int类型的参数,当函数被调用时,编译器传递一个0作为int类型参数的值给该函数:
- //成员函数形式的重载
- < Type > ClassName :: operator ++ ( ); // 前缀
- < Type > ClassName :: operator ++ ( int ); // 后缀
- // 非成员函数形式的重载
- < Type > operator ++ (ClassName & ); // 前缀
- < Type > operator ++(ClassName &,int); // 后缀
在实现中,后缀操作会先构造一个临时对象,并将原对象保存,然后完成自增操作,最后将保存对象原值的临时对象返回。代码如下所示:
- ClassName & ClassName::operator++()
- {
- ClassAdd (1); //increment current object
- return *this; //return by reference the current object
- }
-
- ClassName ClassName::operator++(int unused)
- {
- ClassName temp(*this); //copy of the current object
- ClassAdd (1); //increment current object
- return temp; //return copy
- }
由于前缀操作省去了临时对象的构造,因此它在效率上优于后缀操作。不过,在应用到整型和长整型的操作时,前缀和后缀操作在性能上的区别通常是可以忽略的。但对于用户自定义类型,这还是非常值得注意的。当然就像80-20规则告诉我们的那样,如果在一个很大的程序里,程序数据结构和算法不够优秀,它所能带来的效率提升也是微不足道的,不能使大局有所改变。但是既然它们有差异,我们为什么不在必要的时候采用更有效率的呢?
请记住:
对于整型和长整型的操作,前缀操作和后缀操作的性能区别通常是可以忽略的。对于用户自定义类型,优先使用前缀操作符。因为与后缀操作符相比,前缀操作符因为无须构造临时对象而更具性能优势。