I have two questions about overloading.
1- Why sometimes do make overloading operators non-member functions?
friend Class operator-(const Class &rhs);
2- What's the difference between
Class operator+(const Class &c1, const Class &c2);
and
Class operator+(const Class &rhs);
if I want to add two objects C3 = C1 + C2
?
Any help is appreciated...
If you overload a binary operator as a member function, it ends up asymmetrical: the left operand must be the exact type for which the operator is overloaded, but the right operand can be anything that can be converted to the correct type.
If you overload the operator with a non-member function, then both operands can be converted to get the correct type.
What you have as your second point looks like a concrete example of the same point, not really anything separate at all. Here's a concrete example of what I'm talking about:
class Integer {
int val;
public:
Integer(int i) : val(i) {}
operator int() { return val; }
// Integer operator+(Integer const &other) const { return Integer(val + other.val); }
friend Integer operator+(Integer const &a, Integer const &b) {
return Integer(a.val + b.val);
}
};
int main() {
Integer x(1);
Integer y = x + 2; // works with either operator overload because x is already an Integer
Integer z = 2 + x; // works with global overload, but not member overload because 2 isn't an Integer, but can be converted to an Integer.
return 0;
}
Also note that even though the definition of the friend
function is inside the class definition for Integer
, the fact that it's declared as a friend means it's not a member function -- declaring it as friend
makes it a global function, not a member.
Bottom line: such overloads should usually be done as free functions, not member functions. Providing the user with an operator that works correctly (drastically) outweighs theoretical considerations like "more object oriented". When necessary, such as when the implementation of the operator needs to be virtual, you can do a two-step version, where you provide a (possibly virtual) member function that does the real work, but the overload itself is a free function that invokes that member function on its left operand. One fairly common example of this is overloading operator<<
for a hierarchy:
class base {
int x;
public:
std::ostream &write(std::ostream &os) const {
return os << x;
}
};
class derived : public base {
int y;
public:
std::ostream &write(std::ostream &os) const {
return (base::write(os) << y);
}
};
std::ostream &operator<<(std::ostream &os, base const &b) {
return b.write(os);
}
This supports both polymorphic implementation (and access to a base class' protected members, if necessary) without giving up the normal characteristics of the operator to get it.
The primary exceptions to overloading binary operators as free functions are assignment operators (operator=
, operator+=
, operator-=
, operator*=
and so on). A conversion would produce a temporary object, which you can't assign to anyway, so for this particular case, the overload should be (must be, as a matter of fact) a member function instead.
1-Why sometimes do make overloading operators non-member functions?
It's matter of choice. You can either make the operator +/-
a class member or make it a free friend
function.
The syntax you provided for operator -
is wrong if it's a free friend
function, it should take 2 arguments (similar to operator +
in your example).
2- What's the difference between
As said before, it's just a syntax different. The first one should be a free friend
function and the second one is a class
member.
On top of that, I believe keeping an operator as class
member method is superior compared to free function because:
- Member method can access the
protected
members of base class if
applicable, but the friend
function cannot
- Member method is more object oriented approach because you associate a method which relates to a class
Making the overloaded operator a friend is a better option because take this example
class Comples
{
public:
Complex(float real, float imaginary);
friend operator+ (const Complex &rhs);
...
};
By having the operator a friend you can do the following
Complex result1 = 5.0 + Comples(3.0, 2.0);
and
Complex result1 = Comples(3.0, 2.0) + 5.0;
Which obeys the rules of additions communitivity property. This cannot be achieved if the overloaded operator is a member function. This is due to the compiler able to implicitly create a Complex
object asrequired in this case. Thus the addition operator when being a friend acts in accordance with the normal notion of addition in mathematics.