Key Points
- C++ is considered one of the most powerful languages of the OOP paradigm due to its flexibility and tools.
- Operator overloading in C++ allows us to add new meanings to predefined operators.
- Operator overloading can be used for mathematical operations, string concatenation, and custom behavior for user-defined types.
- Examples of operator overloading include adding two numbers, adding two strings, and subtracting two objects of a user-defined class.
- Operators that can be overloaded include binary arithmetic, unary arithmetic, assignment, bitwise, dereferencing, dynamic memory allocation, subscript, function call, logical, and relational operators.
C++ offers various tools to significantly facilitate our job as programmers, making our development work much more efficient. This flexibility is one of the main reasons why C++ is considered one of the most powerful languages of the OOP paradigm. Today, we’ll learn about operator overloading.
Operator overloading in C++ is essential to creating efficient and versatile programs. This is one of the most interesting cornerstones to learn if you want to master object-oriented programming, so let’s explore this concept. We’ll first approach it conceptually, and then go over a complex application of this tool.
What Is Operator Overloading?
Object-oriented programming allows us to create versatile programs to which we can easily add features just as we can remove them. This paradigm, unlike others, can work with tiny and technical programming tools such as operators.
As you may already know, operators are predefined elements that allow us to perform mathematical or logical operations. They range from simple ones, such as adding and subtracting, to more complicated ones, such as relational and logical operators.
When we overload an operator, we preserve its original function but add a new meaning to it within our specified function. This functionality corresponds to a type of compile-time polymorphism.
So, for example, you could make it so that every time you add two strings, the “+” operator also creates a new string stemming from the result. You can also use it in mathematical operations — you’ll find an example below dealing with this case as well.
Code Examples
First, let’s remember what a simple addition looks like in C++. A classic example is using the “+” operator to concatenate two strings. To give you an idea, this looks more like a mathematical equation than an iteration would. Another one is to simply add two numbers.
This is a very useful tool you have definitely used in your projects, even without knowing what operator overloading is. Here’s what adding an integer and a float can look like.
#include <iostream>
int main() {
int a;
float b, sum;
a = 5; // Assign a value of 5 to 'a'
b = 3.14f; // Assign a value of 3.14 to 'b'
sum = a + b; // Perform addition and store the result in 'sum'
std::cout << "Sum: " << sum << std::endl;
return 0;
}

©History-Computer.com
Here the output is a real number: 8,14. This example shows the straightforward nature of mathematical operators acting as logical operators and vice versa. In this case, addition is equal to the “and” logical operator.
Quick rundown. We have two variables, the first one is an integer, and the second one is a float value. These two variables have built-in data types as values. In this case, we use the “+” operator to add our two variables together to get a new result. As you can see, C++ allows us to carry out this operation without any problem, easily combining built-in data types — this is that versatility we talked about earlier.
You could also use the same idea, as we mentioned, to add together two strings:
#include <iostream>
#include <string>
int main() {
std::string str1 = "I love";
std::string str2 = " History Computer!";
std::string str3 = str1 + str2;
std::cout << str3 << std::endl;
return 0;
}

©History-Computer.com
You can see how we use the addition operator in “str3” to store the result of adding together our two sentences. These examples are simple demonstrations of operators in real-life situations.
Operator Overloading: A Conceptual Example
Now let’s look at a simple operator overloading example. Pay attention to how the subtraction operator is treated here:
class Number {
private:
int value;
public:
Number(int val = 0) : value(val) {}
Number operator-(const Number& other) const {
return Number(value - other.value);
}
};
int main() {
Number num1(10);
Number num2(5);
Number num3 = num1 - num2;
return 0;
}

©History-Computer.com
Explanation of Code
In this case, we overload the “-” operator inside the “number” class. We first define the “Number” class, which contains a variable “value” that represents any integer. We overload the subtraction operator using the “operator-” function, which takes a parameter representing the right-hand side of the operation. It then subtracts the “value” of one object from the “value” of another. It stores the result in a new object and returns it.
In the “main()” function, we create two instances of “number”, the first containing the number 10 and the other the number 5. We subtract “num2” from “num1” and store the result in a new instance called “num3”. Then the code returns 0, meaning it executed successfully.
This code serves as a conceptual example of how operator overloading allows an intuitive usage of operators with user-defined types. We have customized the operator’s behavior for objects within the “number” class.
Overloading the Addition Operator
Let’s go over what applying what we just explained to our previous addition example would look like. Below, we overload the “+” operator. This is much closer to what you’ll be doing in your code, and it includes print statements in case you want to copy it and try it out in your console as you read this.
#include <iostream>
using namespace std;
class Complex {
private:
int real;
int imag;
public:
Complex(int r = 0, int i = 0) {
real = r;
imag = i;
}
Complex operator+(Complex const& obj) {
Complex res;
res.real = real + obj.real;
res.imag = imag + obj.imag;
return res;
}
void print() {
cout << real << " + i" << imag << 'n';
}
};
int main() {
Complex c1(10, 5);
Complex c2(2, 4);
Complex c3 = c1 + c2;
c3.print();
return 0;
}

©History-Computer.com
Explanation of Code
Here, we overload the “+” operator to add a real and an imaginary number together, forming a complex number. First, we define a class called “complex” which contains two variables: “real”, and “imag”. The first represents any real number (integers, fractions, etc), and the second is an imaginary number.
Then the “complex” class overloads the addition operator and takes a “const” reference creating a new “complex” object. It then adds together the “real” and “imag” members of the original object and the newly created one. It stores the result in a new object belonging to the “complex” class. Then it prints them.
In the “main” function we create two “complex” objects, c1 and c2, with specific values assigned to them. Then we add them and store them in a new object, “c3”. The “print()” function is called on this object to output the sum of the complex numbers. Here the output should be “12 + i9″.
As you can see, the possibilities are endless: advanced C++ programmers easily handle overloaded operators, allowing for efficient, easy-to-understand programs.
Complex Operator Overloading Example
Finally, let’s look at how operator overloading could be used to solve a real mathematical problem you might encounter working with vectors and matrices. This is as close as you’ll get to an actual programming interview problem. We’ll add print statements so you can copy the code and analyze it along with our explanation.
#include <iostream>
#include <vector>
using namespace std;
class Matrix {
private:
vector<vector<int>> data;
public:
Matrix() {}
Matrix(const vector<vector<int>>& inputData) : data(inputData) {}
Matrix operator+(const Matrix& other) const {
Matrix result;
int rows = data.size();
int cols = data[0].size();
for (int i = 0; i < rows; i++) {
vector<int> newRow;
for (int j = 0; j < cols; j++) {
int sum = data[i][j] + other.data[i][j];
newRow.push_back(sum);
}
result.data.push_back(newRow);
}
return result;
}
Matrix operator*(const Matrix& other) const {
Matrix result;
int rows = data.size();
int cols = other.data[0].size();
int commonSize = data[0].size();
for (int i = 0; i < rows; i++) {
vector<int> newRow;
for (int j = 0; j < cols; j++) {
int sum = 0;
for (int k = 0; k < commonSize; k++) {
sum += data[i][k] * other.data[k][j];
}
newRow.push_back(sum);
}
result.data.push_back(newRow);
}
return result;
}
friend ostream& operator<<(ostream& os, const Matrix& matrix) {
for (const vector<int>& row : matrix.data) {
for (int element : row) {
os << element << " ";
}
os << endl;
}
return os;
}
};
int main() {
vector<vector<int>> data1 = {{1, 2, 3}, {4, 5, 6}};
vector<vector<int>> data2 = {{7, 8, 9}, {10, 11, 12}};
Matrix matrix1(data1);
Matrix matrix2(data2);
cout << "Matrix 1:" << endl;
cout << matrix1 << endl;
cout << "Matrix 2:" << endl;
cout << matrix2 << endl;
Matrix matrix3 = matrix1 + matrix2;
cout << "Matrix 1 + Matrix 2:" << endl;
cout << matrix3 << endl;
Matrix matrix4 = matrix1 * matrix2;
cout << "Matrix 1 * Matrix 2:" << endl;
cout << matrix4 << endl;
return 0;
}

©History-Computer.com
Explanation of Code
This code defines a “matrix” class that allows for matrix addition and multiplication. It shows how operator overloading can work in our favor in cases when we need to perform multiple operations within the same “main()” function. Additionally, it prints the results. Let’s go over it in a little more detail.
The “matrix” class is defined with a “data” variable inside it, which is a 2d vector of integers. Then we provide a default constructor “matrix()” and another one with our defined variables. That’s “Matrix(const vector<vector<int>>& inputData)”, and it starts up the “data” variable with our inputted data. Afterward, we overload the “+” operator with the “operator+” function, which performs an element-wise addition of two matrices by iterating over their rows and columns and adding the elements. We store the result in a “matrix” object and return it.
Then we also overload the multiplication operator with the “operator*” function. It does the same as the addition function by iterating through the elements of our matrices. It stores the result and returns it. The “ostream& operator<<” friend function overloads the stream insertion operator, allowing our “matrix” object to be printed using “cout”. The code then iterates through the elements of the “matrix” object and prints each one.
In the “main()” function, we define two sets of input data: “data1” and “data2”, created as 2D vectors. “Matrix1” and “matrix2” are created with the user’s input data, displayed, and then added together. They are also multiplied, and each result is stored in “matrix3” and “matrix4” respectively. They are both displayed using “cout”, and then the program returns 0, meaning it’s finished executing without errors.
Operators You Can Overload
Below is a list of the available operator classes that you can overload, and the characters that represent them. We recommend bookmarking this for a while until you get a hang of which operators you can overload in your code and which ones you can’t:
Operator type | Operator |
---|---|
Binary Arithmetic | +, -, *, /, % |
Unary Arithmetic | +, -, ++, — |
Assignment | =, +=,*=, /=,-=, %= |
Bitwise | & , | , << , >> , ~ , ^ |
De-referencing | (->) |
Dynamic memory allocation, De-allocation | New, delete |
Subscript | [] |
Function call | () |
Logical | &, | |, ! |
Relational | >, < , = =, <=, >= |
Operators You Cannot Overload
Just as we have a wide variety of operators we can overload, we must also consider those we cannot.
First, we have the “sizeof” operator. In C++, this operator is responsible for returning the size of an object or data entered as an operand. For example, “sizeof” allows you to calculate the size of an array to know how many items you’ve stored inside it. This operation is so basic to the proper development of a program that modifying its inner workings by overloading it could make your code crash.
“Typeid” is a similar case. It allows us to return the type of an object referred to by a pointer. The detect and identify approach of this operator is very simple. If we wanted to alter its operation, the ideal would be to extend the order chain and alter the result after calling “typeid”. This way the operator remains intact, and the results it returns are accurate.
The dot (.) operator, the scope resolution (::), and the ternary or conditional (?:) also can’t be overloaded.
The nature of these tools is complex and critical to programming. We will not delve into these three elements — but it is enough to know that changing these operators is going against the logic programming paradigm.
Wrapping Up
Operator overloading in C++ is essential for anyone looking to master the language. It allows us to load new meanings into predefined operators, such as addition and subtraction.
In order to be overloaded, operators must be used in operations that use user-defined classes and variables created from them. We can only overload native operators in C++, and you can’t change their arity. In computer science, arity refers to the number of arguments used by any function or operation.
You cannot overload all operators. When you’re coding, we recommend keeping the table we provided as a reference so as to avoid any future debugging.
These are some of the most important points to keep in mind, now is the time to put these concepts to the test. If you want to expand your knowledge on any of the topics we covered, we recommend you visit the official C++ documentation.
The image featured at the top of this post is ©Maria Vonotna/Shutterstock.com.