codingstuff.io
ExploreTutorialsProblemsCS Subjects
Get Started
ExploreTutorialsProblemsCS Subjects
Get Started
codingstuff.io

Master the art of building software through interactive tutorials, real-world problems, and guided projects.

Pune, Maharashtra, India

codingstuffmail@gmail.com

Product

  • Explore
  • Tutorials
  • Problems
  • CS Subjects

Company

  • About
  • Contact
  • Privacy Policy
  • Terms & Conditions
  • Sitemap

© 2026 codingstuff.io. All rights reserved.

Built with ❤️ for developers everywhere

/
/
All Tutorials
⚡

C++ Programming

47 / 87 topics
38OOP Concepts Overview39Classes and Objects40Class Methods41Constructors & Constructor Overloading42Destructors43Access Modifiers / Specifiers44Encapsulation45Abstraction46Friend Functions and Friend Classes47Operator Overloading
Tutorials/C++ Programming/Operator Overloading
⚡C++ Programming

Operator Overloading

Updated 2026-05-12
30 min read

Operator Overloading

In C++, operator overloading is a powerful feature that allows you to redefine the behavior of operators for user-defined types. This can make your code more intuitive and easier to understand by allowing objects of these types to be used with standard operators in a natural way. For example, you might want to add two complex numbers or compare two strings using the == operator.

In this tutorial, we'll explore how to overload various operators such as +, -, ==, <<, >>, [], and (). We'll also discuss the rules and restrictions for operator overloading, the difference between member and non-member functions for overloading, and provide a practical example to solidify your understanding.

Introduction

Operator overloading allows you to define how operators work with user-defined types. This can make your code more intuitive and easier to read. For instance, instead of writing complexNumber1.add(complexNumber2), you can write complexNumber1 + complexNumber2. This makes the code look more like natural mathematical expressions.

However, operator overloading should be used judiciously. Overusing it or misusing it can lead to confusing and hard-to-maintain code. It's important to understand the rules and restrictions for operator overloading to use it effectively.

Core Content

Basic Syntax and Rules

To overload an operator, you define a function with a special name that includes the operator keyword followed by the operator symbol. This function can be either a member function or a non-member function.

Member Functions

When an operator is overloaded as a member function, it must have at least one operand of the class type on which the operator is being defined. The syntax for defining an operator as a member function is:

C++
1class ClassName {
2public:
3 ReturnType operator OperatorSymbol(Parameters) {
4 // Function body
5 }
6};

Non-Member Functions

When an operator is overloaded as a non-member function, it can have both operands of the class type or one operand of the class type and another operand of any other type. The syntax for defining an operator as a non-member function is:

C++
1ReturnType operator OperatorSymbol(Parameters) {
2 // Function body
3}

Commonly Overloaded Operators

Let's explore some commonly overloaded operators with examples.

Overloading + (Addition)

Here's how you can overload the + operator for a simple ComplexNumber class:

complex.cpp
1#include <iostream>
2
3class ComplexNumber {
4private:
5 double real;
6 double imag;
7
8public:
9 ComplexNumber(double r = 0.0, double i = 0.0) : real(r), imag(i) {}
10
11 // Overload the + operator as a member function
12 ComplexNumber operator+(const ComplexNumber& other) const {
13 return ComplexNumber(real + other.real, imag + other.imag);
14 }
15
16 void display() const {
17 std::cout << real << " + " << imag << "i" << std::endl;
18 }
19};
20
21int main() {
22 ComplexNumber c1(3.0, 4.0);
23 ComplexNumber c2(1.5, -2.5);
24
25 ComplexNumber result = c1 + c2;
26 result.display(); // Output: 4.5 + 1.5i
27
28 return 0;
29}
Output
4.5 + 1.5i

Overloading == (Equality)

Here's how you can overload the == operator to compare two ComplexNumber objects:

complex_equal.cpp
1#include <iostream>
2
3class ComplexNumber {
4private:
5 double real;
6 double imag;
7
8public:
9 ComplexNumber(double r = 0.0, double i = 0.0) : real(r), imag(i) {}
10
11 // Overload the == operator as a member function
12 bool operator==(const ComplexNumber& other) const {
13 return (real == other.real && imag == other.imag);
14 }
15
16 void display() const {
17 std::cout << real << " + " << imag << "i" << std::endl;
18 }
19};
20
21int main() {
22 ComplexNumber c1(3.0, 4.0);
23 ComplexNumber c2(3.0, 4.0);
24
25 if (c1 == c2) {
26 std::cout << "The complex numbers are equal." << std::endl;
27 } else {
28 std::cout << "The complex numbers are not equal." << std::endl;
29 }
30
31 return 0;
32}
Output
The complex numbers are equal.

Overloading << and >> (Input/Output)

Here's how you can overload the << and >> operators for a simple ComplexNumber class:

complex_io.cpp
1#include <iostream>
2
3class ComplexNumber {
4private:
5 double real;
6 double imag;
7
8public:
9 ComplexNumber(double r = 0.0, double i = 0.0) : real(r), imag(i) {}
10
11 // Overload the << operator as a friend function
12 friend std::ostream& operator<<(std::ostream& os, const ComplexNumber& c) {
13 os << c.real << " + " << c.imag << "i";
14 return os;
15 }
16
17 // Overload the >> operator as a friend function
18 friend std::istream& operator>>(std::istream& is, ComplexNumber& c) {
19 std::cout << "Enter real part: ";
20 is >> c.real;
21 std::cout << "Enter imaginary part: ";
22 is >> c.imag;
23 return is;
24 }
25};
26
27int main() {
28 ComplexNumber c;
29 std::cout << "Enter a complex number:" << std::endl;
30 std::cin >> c;
31
32 std::cout << "You entered: " << c << std::endl;
33
34 return 0;
35}
Output
Enter a complex number:
Enter real part: 3.5
Enter imaginary part: -2.5
You entered: 3.5 + -2.5i

Overloading [] (Subscript)

Here's how you can overload the [] operator for a simple Array class:

array.cpp
1#include <iostream>
2#include <stdexcept>
3
4class Array {
5private:
6 int* data;
7 size_t size;
8
9public:
10 Array(size_t s) : size(s), data(new int[s]) {}
11
12 ~Array() {
13 delete[] data;
14 }
15
16 // Overload the [] operator as a member function
17 int& operator[](size_t index) {
18 if (index >= size) {
19 throw std::out_of_range("Index out of range");
20 }
21 return data[index];
22 }
23
24 const int& operator[](size_t index) const {
25 if (index >= size) {
26 throw std::out_of_range("Index out of range");
27 }
28 return data[index];
29 }
30};
31
32int main() {
33 Array arr(5);
34 for (size_t i = 0; i < 5; ++i) {
35 arr[i] = static_cast<int>(i * 10);
36 }
37
38 for (size_t i = 0; i < 5; ++i) {
39 std::cout << "arr[" << i << "] = " << arr[i] << std::endl;
40 }
41
42 return 0;
43}
Output
arr[0] = 0
arr[1] = 10
arr[2] = 20
arr[3] = 30
arr[4] = 40

Overloading () (Function Call)

Here's how you can overload the () operator for a simple Callable class:

callable.cpp
1#include <iostream>
2#include <string>
3
4class Callable {
5private:
6 std::string name;
7
8public:
9 Callable(const std::string& n) : name(n) {}
10
11 // Overload the () operator as a member function
12 void operator()(const std::string& message) const {
13 std::cout << name << ": " << message << std::endl;
14 }
15};
16
17int main() {
18 Callable greet("Hello");
19 greet("World!"); // Output: Hello: World!
20
21 return 0;
22}
Output
Hello: World!

Rules and Restrictions

  1. Not all operators can be overloaded: You cannot overload the following operators:

    • :: (scope resolution)
    • . (member access)
    • .* (pointer to member access)
    • ?: (ternary conditional)
  2. Operator precedence and associativity remain unchanged: Overloading an operator does not change its precedence or associativity.

  3. At least one operand must be of the class type: You cannot overload operators where both operands are of built-in types.

  4. Cannot change the number of operands: An overloaded operator must have the same number of operands as the original operator.

  5. Must return a value: Overloaded operators must return a value, except for void functions like operator() when used for function calls.

Member vs Non-Member Functions

  • Member Functions: Can access private and protected members of the class. They have one implicit parameter (this). Use member functions when the operation involves modifying the object's state or accessing its private data.
C++
1ComplexNumber operator+(const ComplexNumber& other) const;
  • Non-Member Functions: Do not have access to private and protected members of the class. They are typically used when the operation is symmetric between two operands, such as +, -, *, /. Use non-member functions for operations that do not modify the object's state.
C++
1friend std::ostream& operator<<(std::ostream& os, const ComplexNumber& c);

Common Mistakes and Pitfalls

  1. Incorrect return type: Ensure that the overloaded operator returns the correct type. For example, + should return an object of the same class.

  2. Not handling all cases: When overloading operators like ==, ensure you handle all possible cases to avoid undefined behavior.

  3. Improper use of friends: Overusing friend functions can lead to tight coupling and make your code harder to maintain. Use them judiciously.

Practical Example

Let's create a practical example that combines several operator overloads for a simple Matrix class:

matrix.cpp
1#include <iostream>
2#include <vector>
3
4class Matrix {
5private:
6 std::vector<std::vector<int>> data;
7 size_t rows, cols;
8
9public:
10 Matrix(size_t r, size_t c) : rows(r), cols(c), data(r, std::vector<int>(c, 0)) {}
11
12 // Overload the + operator as a member function
13 Matrix operator+(const Matrix& other) const {
14 if (rows != other.rows || cols != other.cols) {
15 throw std::invalid_argument("Matrix dimensions must match for addition");
16 }
17 Matrix result(rows, cols);
18 for (size_t i = 0; i < rows; ++i) {
19 for (size_t j = 0; j < cols; ++j) {
20 result.data[i][j] = data[i][j] + other.data[i][j];
21 }
22 }
23 return result;
24 }
25
26 // Overload the << operator as a friend function
27 friend std::ostream& operator<<(std::ostream& os, const Matrix& m) {
28 for (const auto& row : m.data) {
29 for (int val : row) {
30 os << val << " ";
31 }
32 os << std::endl;
33 }
34 return os;
35 }
36
37 // Overload the >> operator as a friend function
38 friend std::istream& operator>>(std::istream& is, Matrix& m) {
39 for (size_t i = 0; i < m.rows; ++i) {
40 for (size_t j = 0; j < m.cols; ++j) {
41 is >> m.data[i][j];
42 }
43 }
44 return is;
45 }
46
47 // Overload the [] operator as a member function
48 std::vector<int>& operator[](size_t index) {
49 if (index >= rows) {
50 throw std::out_of_range("Row index out of range");
51 }
52 return data[index];
53 }
54
55 const std::vector<int>& operator[](size_t index) const {
56 if (index >= rows) {
57 throw std::out_of_range("Row index out of range");
58 }
59 return data[index];
60 }
61};
62
63int main() {
64 Matrix m1(2, 3);
65 std::cout << "Enter elements for matrix 1:" << std::endl;
66 std::cin >> m1;
67
68 Matrix m2(2, 3);
69 std::cout << "Enter elements for matrix 2:" << std::endl;
70 std::cin >> m2;
71
72 try {
73 Matrix result = m1 + m2;
74 std::cout << "Result of addition:" << std::endl;
75 std::cout << result;
76 } catch (const std::exception& e) {
77 std::cerr << "Error: " << e.what() << std::endl;
78 }
79
80 return 0;
81}
Output
Enter elements for matrix 1:
1 2 3
4 5 6
Enter elements for matrix 2:
7 8 9
10 11 12
Result of addition:
8 10 12
14 16 18

Summary

  • Operator overloading allows you to redefine the behavior of operators for user-defined types.
  • Member functions can access private members and are used when the operation involves modifying the object's state.
  • Non-member functions do not have access to private members but are useful for symmetric operations.
  • Rules and restrictions must be followed to ensure that overloaded operators behave as expected.
  • Common mistakes include incorrect return types, not handling all cases, and overusing friends.

What's Next?

In the next topic, we'll explore Inheritance Basics, where you'll learn how to create a hierarchy of classes and use inheritance to promote code reuse and organize your code more effectively. This will be a foundational concept for understanding more advanced object-oriented programming techniques in C++.


PreviousFriend Functions and Friend ClassesNext Inheritance Basics

Recommended Gear

Friend Functions and Friend ClassesInheritance Basics