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

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

一、问题引入

举个例子,如果你设计一个表示有理数的类,允许从整型到有理数的隐式转换应该是合理的。在C++内置类型中,从int转换到double也是再合理不过的了(比从double转换到int更加合理)。看下面的例子:

class Rational
{
public:
    //构造函数未设置为explicit,因为我们希望一个int可以隐式转换为Rational
    Rational(int numerator = 0, int denominator = 1);
    int numerator()const;
    int denominator()const; 
    const Rational operator*(const Rational& rhs)const;
private:
    ...
};

你想支持有理数的算术运算,比如加法,乘法等等,跟随直觉,我们将函数放进相关 class 内(有时会与面向对象守则发生矛盾,详见条款23),会发生什么?

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

看到以上结果,也许会觉得满足了,但当你进一步尝试混合模式的运算的时候,你会发现只有一半的操作是对的:

Rational res = oneHalf * 2;//正确
Rational result = 2 * oneHalf; //错误

为什么错误?

二、归因分析

将上面的例子用等价的函数形式写出来,你就会知道问题出在哪里:

result = oneHalf.operator*(2); // fine
result = 2.operator*(oneHalf ); // error!

在此分析:

  1. 第一个能通过,其原因在于发生了隐式类型转换,编译器知道函数需要 Rational 类型,但你传递了 int 类型的实参,它们也同样知道通过调用 Rational 的构造函数,可以将你提供的 int 实参转换成一个 Rational 类型实参,这就是编译器所做的。类似于:
const Rational temp(2); // 创建一个临时变量
result = oneHalf * temp; // 等同于oneHalf.operator*(temp);
  1. 第二不能通过,其原因在于 oneHalf 对象是 Rational 类的一个实例,而 Rational 支持 operator 操作,所以编译器能调用这个函数。然而,整型 2 却没有关联的类,也就没有 operator 成员函数。编译器实际会去寻找非成员operator*函数,例如:
result = operator*(2, oneHalf ); 

因此,为了支持混合模式的运算和满足一致性,为了解决 只有参数列表中的参数才有资格进行隐式类型转换,而 this 指针指向的那个,没有资格进行隐式类型转换 的问题,就要采用non-member函数。

三、解决方案

例如,下面将 operator*() 函数变为一个非成员函数:

class Rational
{
public:
    Rational(int numerator = 0, int denominator = 1);
    int numerator()const;
    int denominator()const;
private:
   	...
};
 
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;
result = 2 * oneFourth;

问题解决,但还有点要注意:operator* 是否该成为Rational class的一个友元函数呢?

答案是否定的,因为 operator* 可以完全依靠Rational的public接口来实现。上面的代码就是一种实现方式。我们能得到一个很重要的结论:成员函数的反义词是非成员函数而不是友元函数

太多的C++程序员认为一个类中的函数如果不是一个成员函数(举个例子,需要为所有参数做类型转换),那么他就应该是一个友元函数。

上面的例子表明这样的推理是有缺陷的。尽量避免使用友元函数,就像生活中的例子,朋友带来的麻烦可能比从它们身上得到的帮助要多。

四、总结

如果你需要为某个函数的所有参数(包括被this这孩子很所指的那个隐喻参数)进行类型转换,那么这个函数必须是个non-member。

  • 15
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值