条款24:若所有参数皆需类型转换,请为此采用non-member函数

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、本条款注意点

当进入泛型编程时,本条款可能就不太适用了。

原文地址:https://www.cnblogs.com/lasnitch/p/12764171.html