读书笔记《Effective C++》条款24:若所有参数皆需类型转换,请为此采用non-member函数

来源:互联网 发布:金隅悦城丽悦园网络 编辑:程序博客网 时间:2024/05/22 10:44

先看个例子:

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

想支持算数运算诸如加法、乘法等等,但不确定是否该由member函数、non-member函数,或可能的话由non-member friend函数来实现它们。先研究以下将operator*写成Rational成员函数的写法:

class Rational {public:Rational(int numerator = 0, int denominator = 1);int numerator() const;int denominator() const;const Rational operator*(const Rational& rhs) const;private:};
这个设计能够将两个有理数以最轻松自在的方式相乘:

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

如果要支持混合式运算,比如拿Rational和int相乘。然而当尝试混合式算术,发现只有一半行得通:

result = oneHalf * 2;//很好result = 2 * oneHalf;//错误!

为什么行不通呢?当以对应的函数形式重写上述两个式子,问题所在便一目了然了:

result = oneHalf.operator*(2);//很好result = 2.operator*(oneHalf);//错误!

oneHalf是一个内含operator*函数的class对象,所以编译器调用该函数。然而证书2并没有相应的class,也就没有operator*成员函数,编译器也会尝试寻找可被以下这般调用的non-member operator*(也就是在命名空间内或在global作用域内):

result = operator*(2, oneHalf);//错误!
但本例并不存在这样一个接受int和Rational作为参数的non-member operator*,因此查找失败。

再次看看result = oneHalf.operator*(2),注意参数是整数2,但Rational::operator*需要的实参却是个Rational对象。在这里可以接受参数整数2,是因为发生了隐式类型转换。编译器知道正在传递一个int,而函数需要的是Rational;但它也知道之需要调用Rational构造函数并赋予你所提供的int,就可以变出一个适当的Rational来。如果Rational构造函数是explicit,则以下语句都不能编译通过:

result = oneHalf * 2;//编译错误,在explicit构造函数的情况下,无法将2转换为一个Rationalresult = 2 * oneHalf;//一样的错误,一样的问题

我们来分析下,为什么即使Rational构造函数不是explicit,仍然只有一个可通过编译,另一个不可以:

result = oneHalf * 2;//没问题(在non-explicit构造函数的情况下)result = 2 * oneHalf;//错误(甚至在non-explicit构造函数的情况下)
结论是,只有当参数被列于参数列内,这个参数才是隐式类型转换的合格参与者。

如果一定要支持混合式算术运算。可行之道:让operator*成为一个non-member函数,允许编译在每一个实参身上执行隐式类型转换:

class Rational {//不再包括operator*};const Rational operator*(const Rational& lhs, const Rational& rhs)//现在成了一个non-member函数{        return Rational(lhr.numerator() * rhs.numerator(), lhs.denominator() * rhs.denominator());}Rational oneFourth(1, 4);Rational result;result = oneFourth * 2;//OKresult = 2 * oneFourth;//OK,通过编译!
不过还有一点需要考虑:operator*是否应该成为Rational class的一个friend函数呢?

就这个例子而言答案是否定的,因为operator*可以完全由Rational的public接口完成任务,上面代码已表明此中做法。这导出一个重要的观察:member函数的反面是non-member函数,不是friend函数。无论何时如果可以避免friend函数就该避免,friend带来的麻烦往往多过其价值。当然有时候friend有其正当性,但这个事实依然存在:不能够只因函数不该成为member,就自动让它成为friend。


要点:

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

阅读全文
0 0
原创粉丝点击