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.
Example: In this example, the + operator is overloaded to add two Number objects.
#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.
Operator Functions vs 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() |
Operators That Cannot Be Overloaded
The following operators cannot be overloaded in C++:
- sizeof
- typeid
- Scope resolution (::)
- Class member access operators (. and .*)
- Ternary / conditional operator (?:)
Why These Operators Cannot Be Overloaded?
1. sizeof Operator
- Evaluated at compile time
- Used internally for memory layout and pointer arithmetic
- Overloading would break fundamental language behavior
2. typeid Operator
- Used for runtime type identification
- Must uniquely identify a type
- Altering its meaning would break RTTI and polymorphism guarantees
3. Scope resolution (::) Operator
- Works on names, not values
- Fully resolved at compile time
- There is no syntax to intercept or overload name resolution
4. Class Member Access Operators (. and .*)
- Implicitly used by the compiler to access members
- Overloading would break object access semantics
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:
- "c1 + c2" is translated internally to "c1.operator+(c2)"
- The dot operator is implicitly required to invoke member functions
- Since this operator is fundamental to object access, it cannot be overloaded
5. Ternary or conditional (?:) Operator
- Evaluates only one of the two expressions based on a condition
- Function calls cannot enforce short-circuit evaluation
- Overloading would break conditional execution guarantees
Important Points About Operator Overloading
- At least one operand must be a user-defined type
- Operators can be overloaded as member or non-member functions
- Some operators (like conversion operators) must be member functions
Conversion Operator Example:
#include <iostream>
using namespace std;
class Fraction
{
private:
int num, den;
public:
Fraction(int n, int d)
{
num = n;
den = d;
}
// Conversion operator
operator float() const
{
return float(num) / float(den);
}
};
int main()
{
Fraction f(2, 5);
float val = f;
cout << val << endl;
}
Output
0.4
Note: Conversion operators must be member functions.
Conversion Constructors
Any constructor that can be called with a single argument acts as a conversion constructor and enables implicit type conversion.
#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