自增、自减的前缀即先增/减后取回
,后缀则是取回然后增/减
。
运算符重载差异
为了区分前缀和后缀,后缀形式的重载函数有int
型参数,在调用处编译器会传一个0
作为函数参数。这个参数没有实际用途,为了避免编译器对未使用参数的警告,应避免在声明和实现给这个参数命名。
class UPInt{
public:
UPInt & operator++(); //++前缀
const UPInt operator++(int); //++后缀 省略参数名
UPInt & operator--(); //--前缀
const UPInt operator--(int); //--后缀
};
除了在参数,还需要注意返回类型和实现方式的不同。
UPInt & UPInt::operator++()//++前缀
{
*this += 1;//自增
return *this;
}
const UPInt UPInt::operator++(int)//++后缀
{
UPInt old = *this;//拷贝原值
++(*this);//自增
return old;//返回原值
}
前缀形式在处理自增后直接返回。而后缀形式首先拷贝原值,进行自增操作后返回原值的拷贝。返回的拷贝值是常量,目的是避免连续的后缀自增/自减运算。
UPInt++++
相当于UPInt.opeator(0)++.operator(0)++
,第二步自增运算符是对UPInt
的拷贝做运算,实际UPInt
只自增了一次,这和看起来有明显差异。同时,C++也不允许对内置类型做连续的后缀自增/自减操作,后缀运算符重载函数也应返回常量以和内置类型保持一致。
效率差异
后缀形式的重载函数有一次拷贝操作,需要临时对象保持原值。对于内置类型后缀自增运算符相比前缀有额外的一拷贝指令。
mov DWORD PTR [rbp-4], 10
#前缀自增
add DWORD PTR [rbp-4], 1 #值+1
mov eax, DWORD PTR [rbp-4]
mov DWORD PTR [rbp-8], eax
#后缀自增
mov eax, DWORD PTR [rbp-4] #拷贝到eax
lea edx, [rax+1] #值+1
mov DWORD PTR [rbp-4], edx #移动自增后的值到eax
mov DWORD PTR [rbp-8], eax #移动原值到edx
可见,前缀形式的自增运算符效率更高。无论是内置类型还是用户定义类型,都应尽量使用前缀形式的自增/自减运算符。