Step by step introduction into operator overloading

This article explains the concept of operator overloading with the help of an analogy with an ordinary function. It demonstrates the overloading on an 'equal to' operator which is used for a comparison of two Skyscraper objects. Below is the header and source file of the original class Skyscraper (for the moment without operator overloading).

#ifndef SKYSCRAPER_H
#define SKYSCRAPER_H
#include "assert.h"

class Skyscraper
{
public:
    double getHeight() const {return this->height;}
    void setHeight(double height);

private:
    double height;
};

#endif // SKYSCRAPER_H
#include "skyscraper.h"

void Skyscraper::setHeight(double height){
    assert(height >= 0);
    this->height = height;
}

Analogy with an ordinary function

Imagine a situation where you have two skyscrapers and would like to find out whether they have equal height. To accomplish this without any knowledge of operator overloading, the simplest thing to do is to write a function that takes two Skyscrapers as parameters and returns true if they are of equal height, false if they are not. We will denote this function areEqual() and place it into main.cpp. Notice the 'const' keyword in front of the function arguments. This means that neither of the Skyscraper objects can be modified inside the function body.

bool areEqual(const Skyscraper& s1, const Skyscraper& s2){
    if(s1.getHeight() == s2.getHeight())
        return true;
    else
        return false;
}

To see what the function does, we initiate both Skyscraper objects and set their heights to different values. You can see this in main.cpp below. If you run the code, the console will print "The scrapers are not equal". If you change the dimension of s2 to 345.0, the program will print "The scrapers are equal".

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

using namespace std;

bool areEqual(const Skyscraper &s1, const Skyscraper &s2);

int main()
{
    Skyscraper s1;
    s1.setHeight(345.0);
    Skyscraper s2;
    s2.setHeight(351.0);
    bool scrapersEqual = areEqual(s1,s2);
    if(scrapersEqual)
        cout << "The scrapers are equal" << endl;
    else
        cout << "The scrapers are not equal" << endl;
    return 0;
}

bool areEqual(const Skyscraper &s1, const Skyscraper &s2){
    if(s1.getHeight() == s2.getHeight())
        return true;
    else
        return false;
}

Operator overloading

But imagine how nice it would be if we could handle the Skyscrapers the way we handle integers or doubles. Then we could just write if(s1 == s2) and the program would give us the correct reply. Unfortunately, if you try to do that with the current code (below), you will get a compilation error. The compiler does not understand what == means for our Skyscraper class.

int main()
{
    Skyscraper s1;
    s1.setHeight(345.0);
    Skyscraper s2;
    s2.setHeight(351.0);
    if(s1 == s2)
        cout << "The scrapers are equal" << endl;
    else
        cout << "The scrapers are not equal" << endl;
    return 0;
}

To make this possible, we will have to overload the operator == in our Skyscraper class. We do not need much to accomplish this. We only need to declare the overloading of the operator == in the header file skyscraper.h. In the first variant of the problem, we are going to place the procedure for operator overloading after the closing braces of the class, i.e. the procedure won't be a member of the Skyscraper class. I am using this non-member approach to enable an easier transition from a regular function to operator overloading.

#ifndef SKYSCRAPER_H
#define SKYSCRAPER_H
#include "assert.h"

class Skyscraper
{
public:
    double getHeight() const {return this->height;}
    void setHeight(double height);

private:
    double height;
};

bool operator==(const Skyscraper &s1, const Skyscraper &s2);

#endif // SKYSCRAPER_H

Notice, that the function takes exactly the same parameters and has the same body as the original function areEqual() in main.cpp. What changed is the name of the function (areEqual vs operator==). Also notice that for operator overloading the naming matters and for the function to be recognised as operator overloading, the operator keyword followed by the operator itself, is a must. Below is the file skyscraper.cpp.

#include "skyscraper.h"

void Skyscraper::setHeight(double height){
    assert(height >= 0);
    this->height = height;
}

bool operator==(const Skyscraper &s1, const Skyscraper &s2){
    if(s1.getHeight() == s2.getHeight())
        return true;
    else
        return false;
}

Now the main.cpp became just what we wished for at the start and the program compiles just fine.

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

using namespace std;

int main()
{
    Skyscraper s1;
    s1.setHeight(345.0);
    Skyscraper s2;
    s2.setHeight(351.0);
    if(s1 == s2)
        cout << "The scrapers are equal" << endl;
    else
        cout << "The scrapers are not equal" << endl;
    return 0;
}

Operator overloading as part of the class

As you certainly noticed, in the previous section, the operator== wasn't part of the Skyscraper class. This is perfectly legal and also simpler to understand. Once you become more comfortable with operator overloading, you might want to include operator== in your class. That is what I am going to show you now. This part requires a bit more attention, since there will be changes in parameters, as well as in the body of the method.

Firstly, let's look below at the definition of the operator==. Notice, that in this case the operator== appears in the public section of the Skyscraper class. Also, notice that one of the parameters (s1) disappeared. Having only a single parameter, what are we going to compare it with? The answer to that is that we are going to compare it with *this, the object whose method is being defined and which was previously denoted as 's1'. Just to remind you, 'this' is a pointer to the location that holds the object whose method is defined. '*this' is the object at the location 'this'. Notice the extra const after the argument list. That means that the procedure does not modify the object on which it is invoked, i.e what we previously referred to as 's1'.

#ifndef SKYSCRAPER_H
#define SKYSCRAPER_H
#include "assert.h"

class Skyscraper
{
public:
    double getHeight() const {return this->height;}
    void setHeight(double height);
    bool operator==(const Skyscraper &s2)  const;

private:
    double height;
};
#endif // SKYSCRAPER_H

Below you can see the new code for the operator== in the file skycraper.cpp. Since this time, the overloaded operator== forms part of the class, we need to use the scope operator::. Also observe that the following changed in the body of the function compared to the previous case:

#include "skyscraper.h"

void Skyscraper::setHeight(double height){
    assert(height >= 0);
    this->height = height;
}

bool Skyscraper::operator==(const Skyscraper &s2) const {
    if( (*this).height == s2.height)
        return true;
    else
        return false;
}

There is an alternative to writing (*this).height which you will see way more often. The same statement can be written as this->height. Thus, the last piece of code would change to:

bool Skyscraper::operator==(const Skyscraper &s2) const {
    if( (this->height == s2.height)
        return true;
    else
        return false;
}

There are some differences when trying to overload other types of operators, but this example should provide you with sufficient means to discover the differences yourself.


Latest update: 01.09.2015
Created: 2013
© Walletfox.com, 2017