Thedre's some invalid code in your example, as well as bits that won't be useful:
Rational operator*(Rational& rhs); (1) Rational operator*(Rational rhs);
(2) Rational& operator*(Rational rhs); (3) Rational& operator*(Rational& rhs); (4) Rational* operator*(Rational& rhs); (5) Rational* operator*(Rational rhs);
(1) this would work, but you should always strive to pass functuion arguments as const reference, unless they are primitive types. In this case fix that signature this way:
Rational operator*(const Rational& rhs);
(2) you can not define two function signatures that only differ by return type! This is a restriction of the C++ language. Besides, it is a bad idea to return a reference as a result from an operator with two operands: what does the return object refer to? First argument? Second Argument? Something else? If it's the latter, does it need clean-up, and who is responsible for that? Fix this by eliminating the function entirely.
(3) Again, doesn't make sense to return a reference (see 2), and again, the argument should be const reference (see 1). Also, changing an argument type from some type to reference-to-type is not sufficient for the purpose of function overloading! See below.
(4) same as above, only that it makes even less sense to return a pointer than a reference! What does it point to? Is it a heap object, and if so, who is responsible for releasing it?
(5) all of the above...
As pointed out in solution 2, it is a good rule of thumb to only define unary operators and arithmetical assignment operators right within the class, and define binary operators outside of the class. The former will should normally return a reference ( i. e.
*this
), and the latter should simply return an object ( i. e. not a reference or pointer ).
An example, just for the variants of
operator*
:
class Rational {
...
Rational& operator*=(const Rational& rhs);
Rational& operator*=(int rhs);
friend Rational operator*(const Rational& lhs, const Rational& rhs);
friend Rational operator*(const Rational& lhs, int rhs);
friend Rational operator*(int lhs, const Rational& rhs);
...
};
Note that I defined the binary operators as friend functions, i. e. not as a member function of the class, although they are still declared within the class definition. It's also helpful to make them a friend because you probably need to access the private class members.
Also note that the last operator could not be defined as a class member function at all! This is a general problem with binary arithmetic operators, and one of the reasons why you need to define some of them outside the class. Another reason is that an operator may work on two arguments of different class type (e. g. multiply matrix by vector): in that case it is best to not define the operator as a member of function of either operand class.
A final word of advice: do look up articles on member function and operator overloading: they only work if the signatures are sufficiently different to give the compiler a chance to decide when to use which when you call them in your code. Given your overload definitions above and the following code, which of your overloads should be called, and how could the compiler decide which it should be?
Rational a, b;
...
a*b;
1. Return type: it is valid to call a function without checking or assigning it's return type. In this example the compiler won't know what the return type of the operator should be, and it therefore must consider operators with all return types!
2. Argument types: should
a
or
b
be passed by value or by reference? If you define an operator for both variants, the compiler wouldn't know which to use in this example!