Operator Overloading in C++
Operator overloading means giving a new meaning to an operator (like +, -, *, []) when it is used with objects.
- With operator overloading, we can make operators work for user defined classes structures.
- It is an example of compile-time polymorphism.
#include <iostream>
using namespace std;
struct Number
{
int value;
Number(int v)
{
value = v;
}
// Overload + operator
Number operator+(const Number &n)
{
return Number(value + n.value);
}
void display()
{
cout << value << endl;
}
};
int main()
{
Number n1(5), n2(10);
Number n3 = n1 + n2;
n3.display();
}
Output
15
Why use Operator Overloading?
- Allows objects to behave like basic data types.
- Useful for mathematical objects like Complex numbers and Vectors.
- Reduces the need for extra function calls.
Difference between Operator Functions and Normal Functions
Feature | Operator Function | Normal Function |
|---|---|---|
Syntax | Uses operator keyword | Standard function name |
Invocation | Triggered by using an operator | Called explicitly by name |
Purpose | Redefines behavior of operators | Performs defined actions |
Example | operator+() | add() |
Some operators cannot be overloaded, such as sizeof, typeid, Scope resolution (::), Class member access operators (.(dot), .* (pointer to member operator)),Ternary or conditional (?:) .
Why can't the above-stated operators be overloaded?
1. sizeof Operator
This returns the size of the object or datatype entered as the operand. This is evaluated by the compiler and cannot be evaluated during runtime. The proper incrementing of a pointer in an array of objects relies on the sizeof operator implicitly. Altering its meaning using overloading would cause a fundamental part of the language to collapse.
2. typeid Operator
This provides a CPP program with the ability to recover the actually derived type of the object referred to by a pointer or reference. For this operator, the whole point is to uniquely identify a type. If we want to make a user-defined type 'look' like another type, polymorphism can be used but the meaning of the typeid operator must remain unaltered, or else serious issues could arise.
3. Scope resolution (::) Operator
This helps identify and specify the context to which an identifier refers by specifying a namespace. It is completely evaluated at runtime and works on names rather than values. The operands of scope resolution are note expressions with data types and CPP has no syntax for capturing them if it were overloaded. So it is syntactically impossible to overload this operator.
4. Class member access operators (.(dot), .* (pointer to member operator))
The importance and implicit use of class member access operators can be understood through the following example:
#include <iostream>
using namespace std;
class Complex
{
private:
int real;
int imaginary;
public:
Complex(int real, int imaginary)
{
this->real = real;
this->imaginary = imaginary;
}
void print()
{
cout << real << " + i" << imaginary;
}
Complex operator+(Complex c2)
{
Complex c3(0, 0);
c3.real = this->real + c2.real;
c3.imaginary = this->imaginary + c2.imaginary;
return c3;
}
};
int main()
{
Complex c1(3, 5);
Complex c2(2, 4);
Complex c3 = c1 + c2;
c3.print();
return 0;
}
Output
5 + i9
Explanation:
The statement Complex c3 = c1 + c2; is internally translated as Complex c3 = c1.operator+ (c2); in order to invoke the operator function. The argument c1 is implicitly passed using the '.' operator. The next statement also makes use of the dot operator to access the member function print and pass c3 as an argument.
Besides, these operators also work on names and not values and there is no provision (syntactically) to overload them.
5. Ternary or conditional (?:) Operator
The ternary or conditional operator is a shorthand representation of an if-else statement. In the operator, the true/false expressions are only evaluated on the basis of the truth value of the conditional expression.
conditional statement ? expression1 (if statement is TRUE) : expression2 (else)A function overloading the ternary operator for a class say ABC using the definition
ABC operator ?: (bool condition, ABC trueExpr, ABC falseExpr);would not be able to guarantee that only one of the expressions was evaluated. Thus, the ternary operator cannot be overloaded.
Important Points about Operator Overloading
- For operator overloading to work, at least one of the operands must be a user-defined class object.
- Assignment Operator: Compiler automatically creates a default assignment operator with every class. The default assignment operator does assign all members of the right side to the left side and works fine in most cases (this behavior is the same as the copy constructor). See this for more details.
- Conversion Operator: We can also write conversion operators that can be used to convert one type to another type.
#include <iostream>
using namespace std;
class Fraction
{
private:
int num, den;
public:
Fraction(int n, int d)
{
num = n;
den = d;
}
// Conversion operator: return float value of fraction
operator float() const
{
return float(num) / float(den);
}
};
int main()
{
Fraction f(2, 5);
float val = f;
cout << val << '\n';
return 0;
}
Output
0.4
Overloaded conversion operators must be a member method. Other operators can either be the member method or the global method.
- Any constructor that can be called with a single argument works as a conversion constructor, which means it can also be used for implicit conversion to the class being constructed.
#include <iostream>
using namespace std;
class Point
{
private:
int x, y;
public:
Point(int i = 0, int j = 0)
{
x = i;
y = j;
}
void print()
{
cout << "x = " << x << ", y = " << y << '\n';
}
};
int main()
{
Point t(20, 20);
t.print();
t = 30;
t.print();
return 0;
}
Output
x = 20, y = 20 x = 30, y = 0