24 Declare non-member functions when type conversions should apply to all parameters

If you are designing a class to represent rational numbers, allowing implicit conversions from integers to rationals does not seem unreasonable.

Let us see the Rational class :

class Rational{
public:
  Rational(int numberator = 0, int denominator = 1); // not explicit;
  int numberator() const;
  int denominator() const;

private:
};

You would like to support arithmetic operations like addition, multiplication, etc, but you are not sure whether you should implement then via member functions , non-member functions ,or non-member functions that are friends. As the oop, it seems natural to implement operator* for rational numbers inside the Rational class.

class Rational{
public:
  ...
const Rational operator*(const Rational& rhs) const;
};

The design lets you multiply rationals with the greatest of case :

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

It seems well, and you would like to support mixed-mode operations , where Rational can be multiplied with, for example, int.
When you try to do mixed-mode arithmetic, however, you find that it works only half the time :

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

Because these two examples in their equivalent function form :

result = oneHalf.operator*(2); // fine
result = 2.operator*(oneHalf); // error

Because of the implicit type conversion, the first example is like this :

const Rational temp(2);
result = oneHalf * temp;

Compilers do this only because a non-explicit constructor is involved. If Rational 's constructor were explicit , neither of these statements would compile :

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

For the second example, 2 has not corresponding operator* with Rational object and it is not reason to make a int to Rational object.This is another easy question.

You would still like to support mixed-mode arithmetic, however , and the way to do it is by now perhaps clear : make operator* a non-member function, thus allowing compilers to perform implicit type conversions on all arguments :

class Rational{
...
};

const Rational operator*(const Rational& lhs, const Rational& rhs)
{
  return Rational(lhs.numerator() * rhs.numberator(), lhs.denominator() * rhs.denominator() );
}
Rational oneFourth(1,4);
Rational result;
result = oneFourth * 2; // fine
result = 2 * oneFourth; //fine

Conclusion

  • If you need type conversions on all parameters to a function(including the one pointed to by the this pointer), the function must be a non-member.

Example:

class Rational{
    public:
        Rational(int numberator = 0, int denominator = 1){
            this -> numberator = numberator;
            this -> denominator = denominator;
        }
        int Getnumberator() const
        {
            return numberator;
        }
        int Getdenominator() const
        {
            return denominator;
        }
        void show()
        {
            cout << numberator << '\t' << denominator  <<endl;
        }
        const Rational operator*(const Rational& r)
        {
            Rational result(this->numberator * r.Getnumberator(), this->denominator * r.Getdenominator());
            return result;
        }
    private:
        int numberator;
        int denominator;
};
int main(int argc, char const *argv[])
{
    Rational a(10);
    Rational res = a * 2;
    a.show();   //10      1
    res.show();  //20      1
    Rational resv = 2 * a;  //error: invalid operands to binary expression ('int' and 'Rational')
    return 0;
}

And we make some change :
delete the opeartor* in the class Rational and define it as a non-member function

const Rational operator*(const Rational& l, const Rational& r)
{
    Rational ret(l.Getnumberator() * r.Getnumberator(), l.Getdenominator() * r.Getdenominator());
    return ret;
}
int main(int argc, char const *argv[])
{
    Rational a(10);
    Rational res = a * 2;
    a.show();  //10      1
    res.show(); //20      1
    Rational resv = 2 * a;
    resv.show();//20      1
    return 0;
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容