25.若所有参数均需类型转换,请为此采用non-member函数

前言

</br>
前文曾说过,令classes支持隐式转换通常会带来风险,但这也有例外,比如当你建立的是一个数值类型。


问题实例

</br>
假设我们建立了一个有理数类,它允许整数隐式转换为有理数:

1
2
3
4
5
6
7
class Rational{
public:
Rational(int numerator=0,int denominator=1);//刻意不写为explict
int numerator() const;//成员的访问函数
int denominator() const;
...
}

接下来需要纠结的就是运算符到底是写为member还是non-member呢?


问题处理

</br>
首先考虑声明为member函数的情况:

1
const Rational operator* (const Rational& rhs) const;

那么很自然地,两个Rational对象可以很自由地相乘。但你会发现这个函数没法做到混合类型计算,比如说,一个int乘以一个ratioanl对象:
1
2
res = oneHalf * 2;//正确,因为执行了隐式转换
res = 2 * oneHalf;//错误

不能编译的原因很简单,int并没有成员函数operator*。解决的方法也很简单,把operator*声明为non-member函数即可:
1
const Rational operator* (const Rational& lhs,const Rational& rhs);

如此一来,混合类型计算的问题就得到了完美解决,哪怕两个参数都不是Rational,但只要它们存在直接变为Rational对象的隐式转换即可顺利编译与运行。


friend函数

</br>
那运算符是否需要成为friend函数呢?答案也是否定的。friend函数可以直接访问内部数据,这大大降低了封装性。当然friend函数也有优点,但我们应该牢记:不能因为函数不该成为member,就自动让它成为friend.


总结

</br>
本节叙述的内容仅在OO领域内生效,当我们进入template领域并令Rational成为一个class template后,又会出现新的争议、解法、以及设计思路。
但至少在OO领域内我们应当牢记:如果某个函数的所有参数都可能需要进行类型转换,那么这个函数必须是个non-member.