Table of contents
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;
}
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.
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;
}
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);
}
}
return_type operator symbol (arguments) {
// ....
}
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;
}
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;
}
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;
}
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;
};
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);
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
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
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.
// ...
}
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/