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

条款24::若所有参数皆需类型转换,请为此采用 non-member 函数
       一般在class中很少设计支持隐式转换,但一般也有例外,比如对于一些数值类型的的类。
下面看下有理数的class
 class Rational
 {
 public:
	 //没有加explicit,表明该构造函数可以隐式转换参数
	 Rational(const int numerator = 0, const int denominator = 1) : m_num(numerator), m_den(denominator) {}
	 int GetNum() const {return m_num;}
	 int GetDen() const {return m_den;}
 private:
	 int m_num;
	 int m_den;
 };
我们希望为有理数支持乘法运算,我们先使用member函数实现
	 const Rational operator *(const Rational &rhs) const
	 {
		 return Rational(this->m_num * rhs.m_num, this->m_den * rhs.m_den);
	 }
调用
	Rational r1(1, 2);
	Rational r2(3, 4);
	Rational r3 = r1 * r2;
这样调用一切都是正常,但是对于数值,还希望支持混合运算,如int型
	Rational r1(1, 2);
	Rational r2(3, 4);
	Rational r3 = r1 * 2; //ok
	Rational r4 = 2 * r1; //error
第一种调用方式是:r1.operator*2, 由于Rational构造函数定义了默认参数,2在转为Rational类型时,编译器调用Rational构造函数,传入2为第一个参数,第二个参数为默认参数,最后转换的Rational为(2,2),然后与r1相乘。这一个过程就是隐式转换,这也有赖于将构造函数声明为no-explicit。
第二种调用方式是:编译器也尝试寻找整形对应的operator*函数,但没找到,只能报错处理。
       只有当参数被列于参数列(parameterlist) 内,这个参数才是隐式类型转 换的合格参与者。地位相当于"被调用之成员函数所隶属的那个对象"一-即this 对 象一一的那个隐喻参数,绝不是隐式转换的合格参与者。这就是为什么上述第一次调 用可通过编译,第二次调用则否,因为第一次调用伴随一个放在参数列内的参数,第 二次调用则否。
        那如何才能支持混合运算呢?这引出该条款的主题,no-member函数改派上用场了。
 const Rational operator *(const Rational &lhs, const Rational &rhs)
 {
	 return Rational(lhs.GetNum() * rhs.GetNum(), lhs.GetDen() * rhs.GetDen());
 }
	Rational r1(3, 5);
	Rational r2(3, 4);
	Rational r3 = r1 * 2; //ok (6, 5)
	Rational r4 = 2 * r1; //0k (6, 5)
        operator* 是否应该成为Rational class 的一个friend 函数呢?就本例而言不是的,因为operator* 可以完全藉由Rational 的public 接口完成任务。无论何时如果你可以避免friend  函数就该避免,因为就像真实世界一样,朋友带来的麻烦往往多过其价值。当然有时候friend 有其正当性,但这个事实依然存在:不能够只因函数不该成为member. 就自动让它成为friend。

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

猜你喜欢

转载自blog.csdn.net/hualicuan/article/details/27712441