目录条款24若所有参数皆需类型转换请为此采用非成员函数Declare non-member functions when type conversions should apply to all parameters实现1不可行成员函数member function实现2可行推荐非成员非友元函数non-member non-friend function实现3可行不推荐非成员友元函数non-member friend function小结条款24若所有参数皆需类型转换请为此采用非成员函数Declarenon-member functionswhentype conversionsshould apply toall parameters场景描述考虑一个用于表示“有理数rational numbers”的类构造函数用于构造一个有理数对象注构造函数不声明为explicit允许“整数int”隐式转换为“有理数Rational”见“本条款”成员变量私有n有理数的分子numeratord有理数的分母denominator成员函数公有numerator()分子的访问函数denominator()分母的访问函数问题引入现需要实现用于计算两个有理数的乘积的operator*()函数应该将其实现为“成员函数member function”还是为“非成员函数non-member function”进一步地针对非成员函数应该实现为“友元函数friend function”还是为“非友元函数non-friend function”下面将逐一讨论各种实现的可行性参数类型reference-to-const避免构造和析构的调用提高效率见“条款20”返回值类型const避免客户的误操作见“条款3”、by-value见“条款21”函数类型non-member non-friend“本条款”讨论friend or non-friendnon-friend需要访问私有成员变量但提供public接口member or non-membernon-member支持混合式算术实现1不可行成员函数member function分析当犹豫不决时就应该保持面向对象的精神。由于有理数相乘与Rational类有关因此似乎应该在Rational类内实现为有理数实现operator*()。result oneHalf * 2;Rational * int 正确首先编译器将其视为result oneHalf.operator*(2)并尝试在Rational类内查找member operator*()。 →存在编译器成功调用此函数QRational::operator*(const Rational rhs)的形参类型为Rational而传递的实参类型为int为什么可以通过编译并成功调用A此处发生了隐式类型转换implicit type conversion。编译器发现此处正在传递int类型而函数需要Rational类型但它同时也知道只要调用Rational构造函数并以该int作为参数就能构造出一个合法的Rational对象。因此编译器会自动完成这一转换过程即自动调用Rational构造函数根据实参构造一个临时的Rational对象。换言之在编译器眼中这次调用等价于注1重要当构造函数声明为non-explicit时编译器才会进行隐式类型转换。当构造函数声明为explicit时编译器将禁止执行非预期的类型转换此时上述语句不能通过编译。注2重要只有当参数被列于参数列parameter list内这个参数才是隐式转换的合格参与者。换句话说被调用成员函数所隶属的对象即this对象隐喻参数绝不是隐式转换的合格参与者。result 2 * oneHalf;int * Rational 错误首先编译器将其视为result 2.operator*(oneHalf)并尝试在int内查找member operator*()。 →不存在int并没有相应的类也就没有member operator*()其次编译器将其视为operator*(2, oneHalf)并尝试在命名空间内或全局作用域内查找non-member operator*()。 →不存在结论将operator*()实现为member function不支持混合式算术。实现2可行推荐非成员非友元函数non-member non-friend function分析将operator*()实现为non-member函数可以允许编译器在每一个实参上执行隐式类型转换从而实现混合式算术运算。result oneHalf * 2;正确分析略result 2 * oneHalf;正确分析略结论将operator*()实现为non-member function支持混合式算术。实现3可行不推荐非成员友元函数non-member friend function分析operator*()可以但没必要实现为friend函数因为operator*()的内部实现可以完全借由Rational的public接口比如numerator()、denominator()完成任务。无论何时可以避免friend函数就该避免。小结注意规则之改变 取决于 使用哪一部分C见“条款1”当从Object-Oriented C 跨进 Template C并让Rational成为一个class template而非class将会出现一些考虑的新争议、新解法以及设计见“条款46”。请记住如果某个函数的所有参数都需要进行类型转换(包括this指针所指的隐喻参数)则该函数必须是个non-member。