5.3. Operator Overloading

[ fromfile: functions.xml id: operatoroverloading ]

The keyword operator is used in C++ to define a new meaning for an operator symbol such as +, -, =, *, &, etc. Adding a new meaning to an operator symbol is a specialized form of overloading. Overloaded operators provide a more compact syntax for calling functions, leading to more readable code (assuming the operators are used in ways that are commonly understood).

It is possible to overload nearly all the existing operator symbols in C++. For example, suppose that you want to define a class Complex to represent complex numbers.[41] To specify how to do the basic arithmetic operations with these objects, you could overload the four arithmetic operator symbols. In addition, you could overload the insertion symbol, <<, so that it uses a more stream-lined (if you'll pardon the pun) interface.

Example 5.6 shows a class definition with both member and nonmember operators.

Example 5.6. src/complex/complex.h

#include <iostream>
using namespace std;

class Complex {
    // binary nonmember friend function declarations
    friend ostream& operator<<(ostream& out, const Complex& c);
    friend Complex operator-(const Complex& c1, const Complex & c2);
    friend Complex operator*(const Complex& c1, const Complex & c2);
    friend Complex operator/(const Complex& c1, const Complex & c2);

    public:
    Complex(double re = 0.0, double im = 0.0);   1

    // binary member function operators
    Complex& operator+= (const Complex& c);
    Complex& operator-= (const Complex& c);


    Complex operator+(const Complex & c2);       2

private:
    double m_Re, m_Im;
};

1

Default and conversion constructor.

2

This should be a nonmember friend like the other nonmutating operators.

<include src="src/complex/complex.h" href="src/complex/complex.h" id="complexh" mode="cpp"/>


The operators declared in Example 5.6 are all binary (accepting 2 operands). For the member functions, there is only one formal parameter because the first (left) operand is implicit: *this. The definitions of the member operators are shown in Example 5.7.

Example 5.7. src/complex/complex.cpp

[ . . . . ]

Complex& Complex::operator+=(const Complex& c) {
    m_Re += c.m_Re;
    m_Im += c.m_Im;
    return *this;
}

Complex Complex::operator+(const Complex& c2) {
    return Complex(m_Re + c2.m_Re, m_Im + c2.m_Im);
}

Complex& Complex::operator-=(const Complex& c) {
    m_Re -= c.m_Re;
    m_Im -= c.m_Im;
    return *this;
}


<include src="src/complex/complex.cpp" mode="cpp" href="src/complex/complex.cpp" id="complexcppmembers" segid="members"/>


Example 5.8 shows the definitions of the nonmember friend functions. They are defined like ordinary global functions.

Example 5.8. src/complex/complex.cpp

[ . . . . ]


ostream& operator<<(ostream& out, const Complex& c) {
    out << '(' << c.m_Re << ',' << c.m_Im << ')' ;
    return out;
}

Complex operator-(const Complex& c1, const Complex& c2) {
    return Complex(c1.m_Re - c2.m_Re, c1.m_Im - c2.m_Im);
}

<include src="src/complex/complex.cpp" mode="cpp" href="src/complex/complex.cpp" id="complexcppfriends" segid="friends"/>


We have now expressed the mathematical rules that define each of the four algebraic operations in C++ code. These details are encapsulated and hidden so that client code does not need to deal with them. Example 5.9 shows some client code that demonstrates and tests the Complex class.

Example 5.9. src/complex/complex-test.cpp

#include "complex.h"
#include <iostream>

int main() {
    using namespace std;
    Complex c1(3.4, 5.6);
    Complex c2(7.8, 1.2);
    
    cout << c1 << " + " << c2 << " = " << c1 + c2 << endl;
    cout << c1 << " - " << c2 << " = " << c1 - c2 << endl;
    Complex c3 = c1 * c2;
    cout << c1 << " * " << c2 << " = " << c3 << endl;
    cout << c3 << " / " << c2 << " = " << c3 / c2 << endl;
    cout << c3 << " / " << c1 << " = " << c3 / c1 << endl;
    
    return 0;
}

<include src="src/complex/complex-test.cpp" href="src/complex/complex-test.cpp" id="complextestcpp" mode="cpp"/>


Following is the output of the program in Example 5.9.

(3.4,5.6) + (7.8,1.2) = (11.2,6.8)
(3.4,5.6) - (7.8,1.2) = (-4.4,4.4)
(3.4,5.6) * (7.8,1.2) = (19.8,47.76)
(19.8,47.76) / (7.8,1.2) = (3.4,5.6)
(19.8,47.76) / (3.4,5.6) = (7.8,1.2)

There are some limitations on operator overloading. Only built-in operators can be overloaded. It is not possible to introduce definitions for symbols such as $ " ' that do not already possess operator definitions. Furthermore, although new meanings can be defined for built-in operators, their associativity and precedence cannot be changed.

It is possible to overload all the built-in binary and unary operators except for these:

[Tip]Tip

Here is a way to remember which operators can be overloaded. If the symbol has a dot (.) in it anywhere, overloading is probably not allowed.

[Note]Note

Overloading the comma operator is permitted but not recommended until you are a C++ expert.

You can find a complete table of operator symbols and their characteristics in Section 19.1.

[Note]Note

It is possible to define a new meaning for a built-in operator symbol so that it can be used with operands of different types. But it is not possible to change the associativity or the precedence of a built-in operator symbol.



[41] The set of complex numbers comprises all numbers of the form: a + bi where a and b are real numbers and i is the square root of -1. Because that set includes such numbers for which b = 0, this shows that the real numbers are a subset of the complex numbers.

Complex numbers were introduced initially to describe the solutions to equations like

x2 - 6x + 25 = 0

Using the quadratic formula, you can easily determine that the roots of this equation are 3 + 4i and 3 - 4i.