DEV Community

Thomas Brandoli
Thomas Brandoli

Posted on

Understanding Operator Overloading in C++

In this article, you will start to understand operator overloading in C++.

What is it?

Operator Overloading can give a special behavior (or meaning) to an operator when it is applied to an instance of a class or a user-defined data type.

The problem

For example, what happens if we have two instances of a class and we try to add one with another? The compiler in that case doesn't know what to do, then will throw an error.


class Color {
public:
    Color(name) 
        : name(name) {}

    std::string name;
};

int main() {
    Color green("Green");
    Color red("Red");

    Color yellow = green + red;
    // error: no match for 'operator+' (operand types are 'Color' and 'Color')
    return 0;
}
Enter fullscreen mode Exit fullscreen mode

Have you seen it? We are not able to add the colors because the compiler doesn't know the operator's behavior.


Different operators can be overloaded, here are a few:

  • Addition: +

  • Subtraction: -

  • Multiplication: *

  • Division: /

  • Equality: ==

  • Addition assignment: +=

  • Logical AND: &&

How does it work?

Well, let me give you an example.

Do you remember Cell from Dragonball Z? With his Absorption technique, he can absorb an opponent and his strength.

cell absorbs gif

We can try to replicate the Cell Absorption Technique in code by overloading the plus (+) operator.

enum Kind {
    Human,
    Android
};

class Character {
public: 

    Character(string name, Kind kind, float strength)
        : name(name), kind(kind), strength(strength) {}

    string name;
    Kind kind;
    float strength;
    bool isAlive{true};
};

int main() {
    return 0;
}
Enter fullscreen mode Exit fullscreen mode

In the code above we have an Enum, which helps to identify the Character Kind,

and a Character Class with its constructor and some member variables.

Now we can write the operator function inside the class

Character operator+ (Character& c) {
    if(kind == Kind::Android) {
        c.isAlive = false;
        // Here we simply return a Character, but with the strength addition
        return Character(name, kind, strength + c.strength);
    } else {
        // If the charactor is not an Android, just return it.
        return Character(name, kind, strength);
    }
}
Enter fullscreen mode Exit fullscreen mode

🧐
As we can observe, the syntax is the following

return_type operator symbol (arguments) {
    // ....
}
Enter fullscreen mode Exit fullscreen mode

Let's initialize two characters

int main() {
    Character Cell("Cell", Kind::Android, 9000.0);
    // Name: Cell
    // Kind: Android
    // Strength: 9000.0
    // isAlive: true

    Character Krillin("Krillin", Kind::Human, 750.0);
    // Name: Krillin
    // Kind: Human
    // Strength: 750.0
    // isAlive: true

    return 0;
}
Enter fullscreen mode Exit fullscreen mode

Now Cell can use his technique on Krillin!

int main() {
    Character Cell("Cell", Kind::Android, 9000.0);
    Character Krillin("Krillin", Kind::Human, 750.0);

    // Absorption!
    Cell = Cell + Krillin;

    Cell.name; // Cell
    Cell.kind; // Android
    Cell.strength; // 9750.0
    Cell.isAlive; // true

    Krillin.name; // Krillin
    Krillin.kind; // Human
    Krillin.strength; // 750.0
    Krillin.isAlive; // false

    return 0;
}
Enter fullscreen mode Exit fullscreen mode

Pretty straightforward right?

As you can see, the Cell's strength has increased to 9750.0. This was achieved by "absorbing" Krillin's strength with the help of the overloaded plus operator!

Let's put it all together 👇

#include <iostream>
#include <string>
using std::string;

enum Kind {
    Human,
    Android
};

class Character {
public: 

    Character(string name, Kind kind, float strength)
        : name(name), kind(kind), strength(strength) {}

    Character operator+ (Character& c) {
        if(kind == Kind::Android) {
            c.isAlive = false;
            // Here we simply return a Character, but with the strength addition
            return Character(name, kind, strength + c.strength);
        } else {
            // If the charactor is not an Android, just return it.
            return Character(name, kind, strength);
        }
    }

    string name;
    Kind kind;
    float strength;
    bool isAlive{true};
};

int main() {
    Character Cell("Cell", Kind::Android, 9000.0);
    Character Krillin("Krillin", Kind::Human, 750.0);

    // Absorption!
    Cell = Cell + Krillin;

    Cell.name; // Cell
    Cell.kind; // Android
    Cell.strength; // 9750.0
    Cell.isAlive; // true

    Krillin.name; // Krillin
    Krillin.kind; // Human
    Krillin.strength; // 750.0
    Krillin.isAlive; // false

    return 0;
}
Enter fullscreen mode Exit fullscreen mode

Implementing a Vector2

We can write another more practical example by making a simple implementation of a Vector2, let's do it!

(For those who don't know, a Vector2 defines a position in space)

Here we have a class called Vector2

class Vector2 {
public:
    Vector2()
        : x(0.0), y(0.0) {}

    Vector2(int x, int y)
        : x(x), y(y) {}

    // Vector2 + Vector2
    Vector2 operator+ (Vector2 const& _vec2) {
        return Vec2(x + _vec2.x, y + _vec2.y);
    }

    // Vector2 += Vector2
    Vector2& operator+= (Vector2 const& _vec2) {
        x += _vec2.x;
        y += _vec2.y;

        // Return the left-hand Vector2
        return *this;
    }

    // Vector2 == Vector2
    bool operator== (Vector2 const& _vec2) {
        if (x == _vec2.x && y == _vec2.y)
            return true;
        else
            return false;
    }

    float x, y;
};
Enter fullscreen mode Exit fullscreen mode

Now that we have written the behavior of those operators let's see what happens in the code. Assume that we are making a videogame and there are two Actor:

  • AHero

  • AMonster

Those Actors have different positions.

// Initialize actors
Vector2D AHero(3.0, 1.0);
Vector2D AMonster(5.0, -3.0);
Enter fullscreen mode Exit fullscreen mode

Using the + operator

AHero = AHero + Vector2(1.0, 1.0);
// AHero now has a new position

std::cout << AHero.x << " : " << AHero.y << std::endl;
// 4.0 : 5.0
Enter fullscreen mode Exit fullscreen mode

Using the += operator

AHero += Vector2(3.0, -5.0);
// AHero now has a new position

std::cout << AHero.x << " : " << AHero.y << std::endl;
// 6.0 : -4.0
Enter fullscreen mode Exit fullscreen mode

Using the == operator

// Initialize actors
Vector2D AHero(3.0, 1.0);
Vector2D AMonster(5.0, -3.0);

// The hero moves
AHero += Vector2(1.0, 1.0); // 4.0, 2.0

// The monster moves to the hero position
AMonster = AMonster + Vector2(-1.0, 5.0);

// Now we check if the two actor are in the same position.
// In this case, the hero dies.
if (AHero == AMonster) {
    // The monster kills the hero.
    // ...    
} else {
    // The hero will live.
    // ...
}
Enter fullscreen mode Exit fullscreen mode

And that's it.


If you want to see how to overload other operators and have a better understanding of this concept, I left for you some interesting resources.

Introduction to operator overloading:

https://www.learncpp.com/cpp-tutorial/introduction-to-operator-overloading/

TheCherno:

https://www.youtube.com/watch?v=mS9755gF66w

CodeBeauty:

https://youtu.be/BnMnozsSPmw?si=ZORSRExLocjHNJPi

C++ Logical (&&, ||, !) Operator Overloading:

https://www.geeksforgeeks.org/cpp-logical-operator-overloading/

Top comments (0)