We looked at function overloading, initially in the context of having constructors. We also introduced the idea of operator overloading, specifically operator=, for copy assignment and move assignment.
We can overload other operators but we have to be careful. If one operator is overloaded users of our class may assume every other operator is too.
Checking equality is a good example illustrating how operator overloading can help people use our code. We might have a time class and check that two times are equal using the test:
time1.Equals(time2)
Returns a Boolean wheter time1 and time2 are equal. For someone unfamiliar with our class, but familiar with operations on built in types, it would be more natural to use:
time1 == time2
Operator overloading allows you to define operators for your own abstract data types. Although you can define many operators for any C++ class, you need to think whether or not it makes sense.
If you have a class Student how would/should + or – or < be interpreted?
You overload an operator by defining a function with a specific name that makes it clear the function should be used when the operator is used.
objA + objB is interpreted by the compiler as
objA.operator+(objB);
There are some properties of the operator which you cannot change:
If an operator is normally defined to be unary only, then you cannot overload it to be binary.
Some operators are already overloaded with unary and binary versions, such as +.
You can’t change the order of precedence.
The dot (the member access) operator:
(.)
The pointer operator:
(*), however, * for deferencing can be overloaded.
The scope resolution operator
(::)
The conditional operator:
(?:)
Function:
sizeof()
You cannot overload operators using symbols which are not predefined operators in C++ (such as $, @). Operators cannot be overloaded for the basic C++ types.
You might like to swap the definitions of addition and subtraction for int but you cannot.
class ComplexNumber {
int real;
int img;
public:
ComplexNumber() {}
ComplexNumber(int rl, int im): real(rl), img(im){}
};
We can define instances ....
ComplexNumber a(2, 3), b(4, 5), c;
... but we need to define + to deal with ...
c = a + b;
We generally have three ways to define operators:
As member functions.
As friend functions.
Unusually, we might define it as a non-member, non-friend.
Constraints:
A function that overloads =, (), [] or -> for a class must be a member function of this class.
If the left operand of the operator is an object of a different class, the function must be defined as a non-member function, typically but not necessarily as a friend function.
To overload operator+ for ComplexNumber we could use either, but we will look at the straightforward member function first.
A binary operator as a member function. Function prototype:
class ComplexNumber {
int real;
int img;
public:
ComplexNumber() {}
ComplexNumber(int rl, int im): real(rl), img(im){}
ComplexNumber operator+(const ComplexNumber& arg) const;
};
ComplexNumber ComplexNumber::operator + (const ComplexNumber& arg) const
{
ComplexNumber result;
result.real = real + arg.real;
result.img = img + arg.img;
return result;
}
And now we can use it ...
ComplexNumber a(2, 3), b(4, 5), c;
c = a + b;
With the call being to a.operator+(b)
... modifies the nature of the this pointer. Note that this isn’t visible in the function definition, it’s implicit, so adding const is how we can indicate that we aren’t going to change the object the function is called in the context of. It’s pretty standard for get functions.
Objects that are const can only call const member functions.
friend Class operator symbol (const Class &, const Class &);
Function definition:
ClassName operator symbol (const ClassName& obj1, const ClassName& obj2)
{
statements
}
class ComplexNumber {
friend ComplexNumber operator+(const ComplexNumber&, const
ComplexNumber&);
int real;
int img;
public:
ComplexNumber() {}
ComplexNumber(int rl, int im): real(rl), img(im){}
};
What might the implementation look like?
ComplexNumber operator+( const ComplexNumber& obj1, const ComplexNumber& obj2)
{
ComplexNumber result;
result.real = obj1.real + obj2.real;
result.img = obj1.img + obj2.img;
return result;
}
And now we can use it ...
ComplexNumber a(2, 3), b(4, 5), c;
c = a + b;
With the call being to operator+(a,b).
Function prototype:
const Class& operator=(const ClassName &);
Function definition:
const Class& Class::operator=(const ClassName &obj)
{
if (this != &obj) // An example of guard against self-assignment // Carry out assignments...
return( *this );
}
Function prototype:
bool operatorsymbol(const ClassName&) const;
class ComplexNumber {
int real;
int img;
public:
ComplexNumber() {}
ComplexNumber(int rl, int im): real(rl), img(im) {}
bool operator==(const ComplexNumber& arg) const;
};
bool ComplexNumber::operator==(const ComplexNumber& arg) const
{
return ( real==arg.real && img==arg.img );
}