考虑以下类:
class UPInt
{
public:
UPInt();
UPInt(int value);
...
};
const UPInt operator+(const UPInt& lhs, const UPInt& rhs);
UPInt upi1, upi2;
...
UPInt upi3 = upi1 + upi2;
现在考虑下面这些语句:
upi3 = upi1 + 10;
upi3 = 10 + upi2;
这些语句也能够成功运行。方法是通过建立临时对象把整形数 10 转换为 UPInts
。
让编译器完成这种类型转换是确实是很方便,但是建立临时对象进行类型转换工作是有开销的,而我们不想承担这种开销。
大多数 C++程序员希望进行没有临时对象开销的隐式类型转换,我们如何能这么做呢?
仔细想想,我们的目的不是真的要进行类型转换,而是用 UPint
和 int
做为参数调用 operator+
。隐式类型转换只是用来达到目的的手段,但是我们不要混淆手段与目的。
如果我们想要把 UPInt
和 int
对象相加,通过声明如下几个函数达到这个目的,每一个函数有不同的参数类型集:
const UPInt operator+(const UPInt& lhs, const UPInt& rhs);
const UPInt operator+(const UPInt& lhs, int rhs);
const UPInt operator+(int lhs, const UPInt& rhs);
UPInt upi1, upi2;
...
UPInt upi3 = upi1 + upi2;// 正确,没有由 upi1 或 upi2生成的临时对象
upi3 = upi1 + 10; // 正确, 没有由 upi1 or 10生成的临时对象
upi3 = 10 + upi2; //正确, 没有由 10 or upi2 生成的临时对象。
但是注意,一旦你开始用函数重载来消除类型转换,你就有可能这样声明函数,把自己陷入危险之中:
const UPInt operator+(int lhs, int rhs); // 错误!
这个想法是合情合理的。对于 UPInt
和 int
类型,我们想要用所有可能的组合来重载operator
函数。上面只给出了三种重载函数,唯一漏掉的是带有两个 int
参数的 operator
,所以我们想把它加上。
在 C++中有一条规则是每一个重载的 operator
必须带有一个用户定义类型的参数。int
不是用户定义类型,所以我们不能重载 operator
成为仅带有此[int]类型参数的函数。
总结
利用重载避免临时对象的方法不只是用在 operator
函数上。比如在大多数程序中,你想允许在所有能使用 string
对象的地方,也一样可以使用 char*
,反之亦然。任何带有 string
、char*
、complex
参数的函数可以采用重载方式来消除类型转换。
不过,必须谨记 80-20 规则。没有必要实现大量的重载函数,除非你有理由确信程序使用重载函数以后其整体效率会有显著的提高。