Effective C++学习之若所有参数皆需类型转换,请为此采用nonmember函数

假如我们有以下的有理数类:

class Rational {
public:
    Rational(int numerator = 0, int denominator = 1);

    int getnumerator() const;
    int getdenominator() const;
private:
    int numerator;
    int denominator;
};

在条款19中提到过设计class犹如设计type,也就是说我们的Rational类应该提供类似int类型的功能,因此我们必须添加一些运算符重载函数,标准的教科书函数是这样的,你将自己的重载函数声明为了一个类的成员函数,对一些设计来说这样是可以工作的,比如一下的代码:  

Rational oneEight(1, 8);
Rational oneHalf(1, 2);
Rational result = oneHalf*oneEight;

作为一个有理数类我们希望他可以和一个整数相乘,于是我们就写出了以下的代码:

result = oneHalf * 2;
result = 2 * oneHalf;

第一行的代码能够很好的工作,Oops!!!第二行的地方崩溃了,GCC会给出好几屏的错误提示,这点让人崩溃。下面就来简单分析一下为什么一个第一表达式能够正常运行,而第二个表达式不能正常运行。这次就是发生了所谓的隐士类型转换,编译器发现函数形参是一个Rational而实参是一个int后就给你做了隐士的类型转换,于是乎就有了类似以下的代码,他先用实参构造了一个Rational对象,然后再参与到函数中。

const Rational temp(2);
result = oneHalf *temp

对于第二个表达式编译器就无能为力了,2是一个常量他压根就没有this指针也就无法调用类的成员函数。

C++的有些特性很是让人难受,编译器会在你不经意见来一个类型转换。

要实现完全和内置类型一样的乘法,我们有两种手段一种是将运算符重载为类的友元函数:

class Rational {
public:
    Rational(int numerator = 0, int denominator = 1);

    int getnumerator() const;
    int getdenominator() const;
friend const Rational operator*(const Rational& lhs, const Rational& rhs);
private:
    int numerator;
    int denominator;
};

而另外一种方式就是将重载运算符的函数声明为一个non-member函数:

class Rational {
public:
    Rational(int numerator = 0, int denominator = 1);

    int getnumerator() const;
    int getdenominator() const;
private:
    int numerator;
    int denominator;
};
const Rational operator*(const Rational& lhs, const Rational& rhs);

当我看到第二种方式的写法时,对一直将这些函数写成友元函数的我有点小震惊,之后想想这样的设计比起将其声明为友元函数更具有优点,最起码的你不用担心友元函数会破坏你的内部成员了。

这一条给了我一下的启示:  

  1. 这一条给人的第一感觉就是不要固守在别人给你的限定里,该这样做的时候就应该相信自己,有些东西可能违反一些常规,但那是最有效的。

  2.在这一点也体现了提供对原始资源的访问是必须的。这样就可能减少使用friend的次数,避免friend 对数据的破坏是很有必要的。

 

原文地址:https://www.cnblogs.com/lzh2nix/p/3087964.html