举个例子来说明我们这次要面对的问题:
设计一个class用来表现有理数,它应该支持隐式类型转换,虽然我们说令classws支持隐式类型转换通常是个不好的主意。这是为了我们设计的类可以和内置类型进行运算。例如和整型。
class Rational {
pbulic:
Rational(int numerator = 0, int denominator = 1); //注意这个构造函数不为explicit
int numerator() const;
int denominator() const; //两个接口 提供对成员的访问
priavte:
....
};
现在我们想这个class可以支持运算如加法等。问题就来了,这个operator*函数声明为member函数好呢,还是声明为non-member函数好呢?我们通过分别分析这两个函数来给出答案。
1) member函数实现
class Rational {
public:
.....
const Rational operator* (const Rational& rhs) const;
};
这个设计可以使你能够将两个有理数以最自在的方式相乘,例如:
Rational oneEighth(1,8);
Rational oneHalf(1,2);
Rational result = oneHalf * oneEighth; //这样的调用很好
可是当我们希望一个有理数和一个int的数相乘时出现了问题,
result = oneHalf * 2; //很好
result = 2 * oneHalf; //错误
写成函数形式就可以看到问题:
result = oneHalf.operator*(2); //成功
result = 2.operator*(ontHalf); //错误
operator*函数在int中没有上面class中定义形式,所以不能通过编译。这时编译器也会尝试寻找non-member operator*函数。可是本例没有声明这样的函数,所以查找失败。
为什么第一个可以成功呢?2也不是一个Rational类型的对象。这是因为编译器为这个2进行了隐式类型转换。
为什么第一个可以发生隐式类型转换,第二个调用不可以呢?这是因为第一个调用2是作为参数,而第二个不是。
结论是,只有当参数被列于参数列内,这个参数才是隐式类型转换的合格参与者。也就是说它才可以发生隐式类型转换。
可是作为member函数只能接收一个参数,而我们这里需要两个参数都需要能发生隐式类型转换。所以唯一的办法就是使用non-member函数。
1) non-member函数实现
class Rational { ... };
const Rational operator* (const Rational& lhs, const Rational& rhs) //一个non-member函数
{
return Rational(lhs.numerator() * rhs.numerator(),
lhs.denominator()* rhs.denominator());
}
result = oneHalf * 2; //很好
result = 2 * oneHalf; //没问题了 通过编译
这时还有一个问题,是不是该把这个函数声明为friend函数?
答案是否定的。请记住无论何时如果你可以避免friend函数就该避免,因为就像真实世界一样,朋友带来的麻烦往往多过其价值。
总结:
如果你需要为某个函数的所有参数(包括被this指针所指的那个隐喻参数)进行类型转换,那么这个函数必须是个non-member.