
本文共 2155 字,大约阅读时间需要 7 分钟。
1、问题引出
通常,令class支持隐式转换是不好的设计。但是也有例外,最常见的例外是在建立数值类型时。例如设计一个类表示有理数时,允许整数隐式转换为有理数是合理的。
class Rational{ public: Rational(int numerator = 0, int denominator = 1); //刻意不为explicit;允许int-to-Rational隐式转换 int numerator()const; int denominator()const;};
我需要为有理数类实现加法、乘法等,是该写成member函数、non-member、non-friend函数还是non-member、friend函数呢?
答案是:non-member、non-friend函数。
2、写成member函数引发的问题
写成member函数形式如下:
class Rational{ public: Rational(int numerator = 0,int denominator = 1) : x(numerator), y(denominator){ } //构造函数不为explicit,允许int-to-Rational隐式转换 int numerator() const; //分子 int denominator() const; //分母 const Rational operator*(const Rational& rhs) const //有理数的乘法 { return Rational(x*rhs.x, y*rhs.y); }private: int x; int y;};
(1)此时执行普通的运算是没问题的:
Rational oneEight(1, 8); Rational oneHalf(1, 2); Rational result = onHalf*oneEight; result = result*oneEight;
(2)但是执行混合运算时却会发生错误:
result = oneHalf * 2; //很好 result = 2 * oneHalf; //错误
(3)第二句为什么会引发错误呢?
result = oneHalf.operator*(2); result = 2.operator*(oneHalf);
首先,oneHalf确实可以作为operator* 的参数,但是问题是编译器找不到2 所属的类的operator* 的成员方法,所以就会报错。
(4)第一句为什么能够通过编译呢?
答案就是发生了隐式转换,首先oneHalf有成员方法operator* ,它需要一个参数Rational 类对象,但是传递的参数确是一个int 型的2,于是编译器开始查找Rational 的类型转换构造函数,看能否把2转换成Rational 类对象,它找到了,于是产生了一个临时的对象,然后将这个对象传递给了operator*,因此编译通过。这便是所谓的隐式转换。
(5)为什么第二句的参数2不能发生隐式转换呢?
只有当参数被列于参数列内,而不是隐喻参数this时,这个参数才是隐式转换的合格参与者。隐喻参数this不是隐式转换的合格参与者。
如果将类型转换构造函数写成non-explicit 的话,就达成了二者的一致性,即:都不能通过编译。
显然我们的有理数类是应该支持混合运算的,那么我们的设计就需要改进。
将operator* 写成non-member、non-friend函数
class Rational{ public: Rational(int numerator = 0, int denominator = 1) : x(numerator), y(denominator) { } //构造函数不为explicit,允许int-to-Rational隐式转换 int numerator() const //分子 { return x; } int denominator() const //分母 { return y; }private: int x; int y;};const Rational operator*(const Rational& lhs, const Rational& rhs){ return Rational(lhs.numerator()*rhs.numerator(), lhs.denominator()*rhs.denominator());}
3、为什么写成non-friend的,而不写成friend的?
(1)误解
首先,澄清一个误解。member函数的反面不一定是non-member、friend的。而是non-member、non-friend的。
(2)原则
无论何时,我们都应该避免non-member函数写成friend的。当然,也有必须写成friend的场景,friend是有用的。
4、本条款注意点
当进入泛型编程时,本条款可能就不太适用了。