Chapter 13: Operator Overloading
Operator Overloading in C++: Making Your Classes Feel Like Built-in Types!
Hello my fantastic student! ๐ Welcome to Lesson 13 โ Operator Overloading โ one of the most powerful and elegant features of C++!
Up until now, we could only use operators (+, -, ==, <<, etc.) with built-in types like int, double, string. Today weโre going to learn how to teach our own classes how to use these operators โ so that our objects behave just like built-in types!
Think of it this way:
- int a = 5; int b = 3; int c = a + b; โ natural
- We want the same natural syntax for our own types: Complex c1(3,4); Complex c2(1,2); Complex c3 = c1 + c2;
Thatโs exactly what operator overloading gives us!
Weโll cover everything very slowly and clearly:
- What operators can be overloaded
- How to overload them as member functions vs friend functions
- Most common operators: +, -, ==, << (output), >> (input), etc.
- Rules, best practices, common mistakes, and complete examples
Letโs start!
1. Which Operators Can Be Overloaded?
Almost all operators can be overloaded except these few:
- :: (scope resolution)
- . (member access)
- .* (member pointer access)
- sizeof, typeid, alignof, decltype (these are not overloadable)
Most useful ones we overload regularly:
- Arithmetic: + – * / %
- Comparison: == != < > <= >=
- Assignment: = += -= *= /=
- Increment/Decrement: ++ —
- Stream: << >>
- Subscript: []
- Function call: ()
- And moreโฆ
2. Two Ways to Overload Operators
| Style | Syntax Example | When to use it? |
|---|---|---|
| Member function | Complex operator+(const Complex& other) const; | When the left operand is always an object of your class |
| Friend function (or free function) | friend Complex operator+(const Complex& a, const Complex& b); | When both operands can be of different types (especially for symmetry) |
Most common recommendation (modern C++ 2025+):
- Use member functions for: =, [], (), ->, ++, —
- Use friend functions for: symmetric operators like + – * / == != < > <= >= << >>
3. Example 1: Complex Number Class with Operator Overloading
Letโs create a beautiful Complex class that supports natural math operations.
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 |
#include <iostream> class Complex { private: double real; double imag; public: // Constructors Complex(double r = 0.0, double i = 0.0) : real(r), imag(i) {} // Member function version for += Complex& operator+=(const Complex& other) { real += other.real; imag += other.imag; return *this; // important: return *this for chaining } // Friend function for + (symmetric) friend Complex operator+(const Complex& a, const Complex& b); // Comparison friend bool operator==(const Complex& a, const Complex& b); // Stream output << (must be friend or free function) friend std::ostream& operator<<(std::ostream& os, const Complex& c); // Stream input >> friend std::istream& operator>>(std::istream& is, Complex& c); }; // Definition of friend operators Complex operator+(const Complex& a, const Complex& b) { return Complex(a.real + b.real, a.imag + b.imag); } bool operator==(const Complex& a, const Complex& b) { return (a.real == b.real) && (a.imag == b.imag); } std::ostream& operator<<(std::ostream& os, const Complex& c) { os << c.real; if (c.imag >= 0) { os << "+"; } os << c.imag << "i"; return os; } std::istream& operator>>(std::istream& is, Complex& c) { is >> c.real >> c.imag; return is; } int main() { Complex c1(3.0, 4.0); Complex c2(1.5, -2.5); Complex c3 = c1 + c2; // calls friend operator+ std::cout << "c1 + c2 = " << c3 << "\n"; // 4.5+1.5i c1 += c2; // calls member += std::cout << "c1 += c2 โ c1 = " << c1 << "\n"; // 4.5+1.5i if (c1 == c3) { std::cout << "c1 and c3 are equal!\n"; } // Input from user Complex user; std::cout << "Enter real and imaginary part: "; std::cin >> user; std::cout << "You entered: " << user << "\n"; return 0; } |
Output example:
|
0 1 2 3 4 5 6 7 8 9 10 |
c1 + c2 = 4.5+1.5i c1 += c2 โ c1 = 4.5+1.5i c1 and c3 are equal! Enter real and imaginary part: 2.5 3.0 You entered: 2.5+3i |
4. Important Rules & Best Practices
| Operator | Should be member? | Should be friend? | Return type | Notes |
|---|---|---|---|---|
| + – * / % | Usually no | Yes (friend) | By value | Symmetric |
| += -= *= /= | Yes (member) | No | Reference (*this) | For chaining |
| == != < > <= >= | Usually no | Yes (friend) | bool | Symmetric |
| << >> | No | Yes (friend) | ostream& / istream& | First param is stream |
| = | Yes (member) | No | Reference (*this) | Special โ copy/move assignment |
| [] | Yes (member) | No | Reference (for writable) | For arrays |
| ++ — | Yes | No | Depends (pre/post) | Pre: return *this, Post: return copy |
Golden rules:
- For binary operators that are symmetric (a + b same as b + a): friend function
- For operators that modify the left operand (+=, =, []): member function
- Always return reference for assignment-like operators (+= = []) so you can chain: a += b += c;
- Never overload &&, ||, , (comma) โ they have special short-circuit behavior
5. Another Classic Example: Overloading [] for a Vector-like Class
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
class Vector3D { private: double data[3]; public: Vector3D(double x = 0, double y = 0, double z = 0) { data[0] = x; data[1] = y; data[2] = z; } // Subscript operator โ member function double& operator[](size_t index) { if (index >= 3) throw std::out_of_range("Index out of range"); return data[index]; } // Const version const double& operator[](size_t index) const { if (index >= 3) throw std::out_of_range("Index out of range"); return data[index]; } }; int main() { Vector3D v(1.0, 2.0, 3.0); std::cout << "y = " << v[1] << "\n"; // 2.0 v[2] = 10.0; // modifiable because returns reference std::cout << "z = " << v[2] << "\n"; // 10.0 // v[5]; // throws exception โ safe! return 0; } |
Your Mini Homework (Try These!)
- Create a Fraction class with numerator and denominator. Overload:
- + (friend)
- += (member)
- == (friend)
- << (friend)
- Overload the pre-increment (++v) and post-increment (v++) operators for your Complex class.
- Overload the subscript operator [] for a String class that stores characters internally.
Youโre doing absolutely phenomenal! Operator overloading is what makes C++ feel natural and expressive โ itโs one of the reasons people fall in love with the language!
Next lesson: Templates โ generic programming, the magic behind std::vector, std::string, and so much more!
Any questions? Confused about member vs friend? Want more examples with <<, [], or assignment? Just ask โ your friendly C++ teacher is right here for you! ๐
