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

来源:互联网 发布:美豆数据 编辑:程序博客网 时间:2024/05/22 04:38

总结:

       如果你需要在一个函数的所有参数(包括被 this 指针所指向的那个)上使用类型转换,这个函数必须是一个非成员。


让一个类支持隐式类型转换通常是一个不好的主意。当然,这条规则有一些例外:在创建数值类型时。例如,如果你设计一个用来表现有理数的类,允许从整数到有理数的隐式转换:

class Rational {public:Rational(int numerator = 0, int denominator = 1);// 非explicit,允许int-to-Rational隐式转换int-to-Rationalint numerator() const; // 分子和分母的访问函数int denominator() const;private:...};
       应该支持算术运算,比如加法,乘法等等,但不能确定是通过成员函数、非成员函数、还是非成员的友元函数来实现它们。当你摇摆不定的时候,你应该坚持面向对象的原则。于是有理数的乘法与Rational类相关,所以在Rational类的内部实现有理数的operator*似乎更加正常。我们先让operator*成为Rational的一个成员函数:

class Rational {public:...const Rational operator*(const Rational& rhs) const;  //operator*作为成员函数};Rational oneEighth(1, 8);Rational oneHalf(1, 2);Rational result = oneHalf * oneEighth; // fineresult = result * oneEighth; // fine

        这个设计让你在有理数相乘时不费吹灰之力,但你还希望支持混合模式的操作,以便让 Rational能够和其它类型(如int)相乘。毕竟两个数相乘很正常,即使它们碰巧是不同类型的数值:

result = oneHalf * 2; // fineresult = 2 * oneHalf; // error

只有一半行得通,但乘法必须是可交换的。当以对应的函数形式重写上述两个式子,问题所在便一目了然:

result = oneHalf.operator*(2);result = 2.operator*(oneHalf); 

         对象oneHalf是一个包含 operator* 的类实例,所以编译器调用那个函数。然而整数2没有operator*成员函数。编译器同样要寻找可被如下调用的非成员operator*(也就是说,在 namespace 或全局范围内的operator*):

result = operator*(2, oneHalf); 

       但在本例中,没有非成员的接受int和Rational的operator*函数,所以搜索失败。再看那个成功的调用,它的第二个参数是整数2,而Rational::operator*却持有一个 Rational对象作为它的参数。这里发生了隐式类型转换。编译器知道你传递一个int而那个函数需要一个Rational,通过用你提供的int调用Rational的构造函数,它们能做出一个相配的Rational。换句话说,它们将那个调用或多或少看成如下这样:

const Rational temp(2); // 根据2建立一个临时Rational对象result = oneHalf * temp; // 等同于oneHalf.operator*(temp);
            当然,编译器这样做仅仅是因为提供了一个非explicit构造函数。如果Rational的构造函数是explicit,那两句语句都将无法编译,但至少语句的行为保持一致。

        这两个语句一个可以编译而另一个不行的原因在于,当参数列在参数列表中的时候,才有资格进行隐式类型转换现在支持混合运算的方法或许很清楚了:让operator*作为非成员函数,因此就允许将隐式类型转换应用于所有参数

class Rational {... // 不包括operator*};const Rational operator*(const Rational& lhs, const Rational& rhs){return Rational(lhs.numerator() * rhs.numerator(),lhs.denominator()* rhs.denominator());}Rational oneFourth(1, 4);Rational result;result = oneFourth * 2; // fineresult = 2 * oneFourth; // it works!

另外,仅仅因为函数不应该作为成员并不自动意味着它应该作为友元。

关于operator*参考该文章条款21 必须返回对象时,别妄想返回其reference

0 0
原创粉丝点击