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

本文详细解释了在C++中如何在类的成员函数与非成员函数之间选择合适的隐式类型转换,特别是在涉及不同数据类型之间的操作时。通过实例演示了如何支持类与整数、浮点数等不同类型之间的混合运算,并讨论了构造函数的explicit性和非explicit性如何影响此类转换。

总结:

       如果你需要在一个函数的所有参数(包括被 this 指针所指向的那个)上使用类型转换,这个函数必须是一个非成员。


让一个类支持隐式类型转换通常是一个不好的主意。当然,这条规则有一些例外:在创建数值类型时。例如,如果你设计一个用来表现有理数的类,允许从整数到有理数的隐式转换:

class Rational {
public:
	Rational(int numerator = 0, int denominator = 1);
	// 非explicit,允许int-to-Rational隐式转换int-to-Rational
	int numerator() const; // 分子和分母的访问函数
	int denominator() const;

private:
	...
};
       应该支持算术运算,比如加法,乘法等等,但 不能确定是通过成员函数、非成员函数、还是非成员的友元函数来实现它们。当你摇摆不定的时候,你应该坚持面向对象的原则。于是有理数的乘法与Rational类相关,所以在Rational类的内部实现有理数的operator*似乎更加正常。我们先让operator*成为Rational的一个成员函数:

class Rational {
public:
	...
	const Rational operator*(const Rational& rhs) const;  //operator*作为成员函数
};

Rational oneEighth(1, 8);
Rational oneHalf(1, 2);
Rational result = oneHalf * oneEighth; // fine
result = result * oneEighth; // fine

        这个设计让你在有理数相乘时不费吹灰之力,但你还希望支持混合模式的操作,以便让 Rational能够和其它类型(如int)相乘。毕竟两个数相乘很正常,即使它们碰巧是不同类型的数值:

result = oneHalf * 2; // fine
result = 2 * oneHalf; // error

只有一半行得通,但乘法必须是可交换的。当以对应的函数形式重写上述两个式子,问题所在便一目了然:

result = oneHalf.operator*(2);
result = 2.operator*(oneHalf); 

         对象oneHalf是一个包含 operator* 的类实例,所以编译器调用那个函数。然而整数2没有operator*成员函数。编译器同样要寻找可被如下调用的非成员operator*(也就是说,在 namespace 或全局范围内的operator*):

result = operator*(2, oneHalf); 

       但在本例中,没有非成员的接受int和Rational的operator*函数,所以搜索失败。再看那个成功的调用,它的第二个参数是整数2,而Rational::operator*却持有一个 Rational对象作为它的参数。这里发生了隐式类型转换。编译器知道你传递一个int而那个函数需要一个Rational,通过用你提供的int调用Rational的构造函数,它们能做出一个相配的Rational。换句话说,它们将那个调用或多或少看成如下这样:

const Rational temp(2); // 根据2建立一个临时Rational对象
result = oneHalf * temp; // 等同于oneHalf.operator*(temp);
            当然,编译器这样做仅仅是因为提供了一个非explicit构造函数。 如果Rational的构造函数是explicit,那两句语句都将无法编译,但至少语句的行为保持一致。

        这两个语句一个可以编译而另一个不行的原因在于,当参数列在参数列表中的时候,才有资格进行隐式类型转换现在支持混合运算的方法或许很清楚了:让operator*作为非成员函数,因此就允许将隐式类型转换应用于所有参数

class Rational {
	... // 不包括operator*
};

const Rational operator*(const Rational& lhs, const Rational& rhs)
{
	return Rational(lhs.numerator() * rhs.numerator(),
		lhs.denominator()* rhs.denominator());
}
Rational oneFourth(1, 4);
Rational result;
result = oneFourth * 2; // fine
result = 2 * oneFourth; // it works!

另外,仅仅因为函数不应该作为成员并不自动意味着它应该作为友元。

关于operator*参考该文章条款21 必须返回对象时,别妄想返回其reference

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值