Operator Overloading in C++
Prerequisites
1. What is Operator Overloading
Operator overloading allows you to define custom behavior for operators (+, -, *, ==, etc.) on user-defined types.
| Category | Operators |
|---|
| Arithmetic | + - * / |
| Comparison | == != < > <= >= |
| Assignment | = += -= |
| Increment | ++ -- |
| Access | [] () -> |
| Stream | << >> |
Makes code more intuitive and expressive.
✔ Without overloading
✔ With overloading
Cleaner and closer to mathematical notation
2. Operator Overloading
2-1. Basic Syntax
1
2
3
4
| return_type operatorOP(parameters)
{
// implementation
}
|
Example:
1
2
3
4
5
6
7
8
9
10
| class Vec
{
public:
int x, y;
Vec operator+(const Vec& other) const
{
return {x + other.x, y + other.y};
}
};
|
2-2. Member/Non-member
✔ Member function
1
| Vec operator+(const Vec& other) const;
|
Left operand = object itself
✔ Non-member (free function)
1
| Vec operator+(const Vec& a, const Vec& b);
|
More flexible (especially for symmetry)
| Case | Recommendation |
|---|
| Access private members | Member or friend |
| Symmetric operation | Non-member |
Assignment-like (=) | Member |
2-3. Example
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| class Vec
{
public:
Vec(float x, float y) : x(x), y(y) {}
Vec operator+(const Vec& other) const
{
return Vec(x + other.x, y + other.y);
}
Vec operator*(float scalar) const
{
return Vec(x * scalar, y * scalar);
}
public:
float x, y;
};
|
Stream Operator (<<)
✔ Must be non-member
1
2
3
4
5
6
7
| #include <iostream>
std::ostream& operator<<(std::ostream& os, const Vec& v)
{
os << "(" << v.x << ", " << v.y << ")";
return os;
}
|
Comparison Operator
1
2
3
4
| bool operator==(const Vec& other) const
{
return x == other.x && y == other.y;
}
|
Assignment Operator
1
2
3
4
5
6
7
8
9
10
| Vec& operator=(const Vec& other)
{
if (this != &other)
{
x = other.x;
y = other.y;
}
return *this;
}
|
Must return reference
Increment Operator
✔ Prefix
1
2
3
4
5
6
| Vec& operator++()
{
x++; y++;
return *this;
}
|
✔ Postfix
1
2
3
4
5
6
| Vec operator++(int)
{
Vec temp = *this;
++(*this);
return temp;
}
|
int is dummy parameter
Subscript Operator
1
2
3
4
5
6
7
| int& operator[](int index)
{
if (index == 0)
return x;
return y;
}
|
Function Call Operator
1
2
3
4
5
6
7
8
| class Functor
{
public:
int operator()(int x)
{
return x * x;
}
};
|
Return by value (C++17 optimized)
1
2
3
4
| Vec operator+(const Vec& other) const
{
return Vec(x + other.x, y + other.y);
}
|
Copy elision / move optimization
- Use
const& for parameters - Return by value (optimized)
✔ DO
- Keep semantics intuitive
- Use
const correctness - Prefer non-member for symmetric ops
- Keep operators lightweight
❌ DON’T
- Overload in confusing ways
- Hide expensive operations behind operators
- Break expected behavior
Bad Example: unexpected behavior
1
2
3
4
5
| Vec operator+(const Vec& other)
{
sleep(1); // ❌ unexpected behavior
return ...
}
|