C++ Basics · Chapter 12

Operator Overloading and Custom Data Types in C++

Learn how to create your own types using class and make them behave like built-in types by overloading operators like +, ==, and <<.

By Arj Updated July 2025 14 min read No prior experience needed
What you'll learn
  • How to define custom data types using class
  • What operator overloading is and why it's useful
  • How to overload operators like +, ==, and <<
  • How to build intuitive class-based code
  • When to overload operators and when to avoid it

1Why create custom data types?

C++ lets you define your own types using class or struct, which group data and functions together. This allows you to model real-world entities more naturally and write code that is easier to understand and maintain.

Example – modelling a complex number:

cpp
class Complex {
public:
    double real;
    double imag;
};

You now have a new data type: Complex. You can create variables of this type, store them in arrays, and pass them to functions.

Classes vs structs
In C++, class and struct are nearly identical. The only difference is that class members are private by default, while struct members are public by default. We use class when we want to encapsulate data and provide a clear interface.

2What is operator overloading?

Operator overloading allows you to redefine how operators work for your custom types. This makes your code cleaner and more intuitive.

Without operator overloading:

cpp
Complex result = add(c1, c2);

With operator overloading:

cpp
Complex result = c1 + c2;

The second version is much more natural. Anyone reading the code immediately understands that you are adding two complex numbers.

Overload for readability
The goal of operator overloading is to make your custom types behave like built-in types. If overloading an operator makes the code less clear, don't do it.

3Step 1: Define the class

Let's walk through creating a simple Complex number class and overloading some basic operators.

Define the class with a constructor and display function:

cpp
class Complex {
public:
    double real, imag;

    // Constructor
    Complex(double r = 0, double i = 0) {
        real = r;
        imag = i;
    }

    // Display function
    void display() {
        cout << real << " + " << imag << "i" << endl;
    }
};

4Step 2: Overload the + operator

We want to allow c1 + c2 where both are Complex objects. The overloaded operator returns a new Complex object.

cpp
Complex operator+(const Complex& c) {
    return Complex(real + c.real, imag + c.imag);
}

Complete class with + overloaded:

cpp
class Complex {
public:
    double real, imag;

    Complex(double r = 0, double i = 0) {
        real = r;
        imag = i;
    }

    Complex operator+(const Complex& c) {
        return Complex(real + c.real, imag + c.imag);
    }

    void display() {
        cout << real << " + " << imag << "i" << endl;
    }
};

Usage:

cpp
Complex c1(3.5, 2.5);
Complex c2(1.5, 4.5);
Complex result = c1 + c2;
result.display();
Output5 + 7i

5Step 3: Overload the == operator

You can define what it means for two objects to be equal. This makes comparisons intuitive.

cpp
bool operator==(const Complex& c) {
    return real == c.real && imag == c.imag;
}

Usage:

cpp
if (c1 == c2) {
    cout << "Equal\n";
} else {
    cout << "Not Equal\n";
}

6Step 4: Overload the << operator for cout

To use cout << object, you must define operator<< as a friend function outside the class.

cpp
friend ostream& operator<<(ostream& out, const Complex& c) {
    out << c.real << " + " << c.imag << "i";
    return out;
}

Complete updated class:

cpp
class Complex {
public:
    double real, imag;

    Complex(double r = 0, double i = 0) : real(r), imag(i) {}

    Complex operator+(const Complex& c) {
        return Complex(real + c.real, imag + c.imag);
    }

    bool operator==(const Complex& c) {
        return real == c.real && imag == c.imag;
    }

    friend ostream& operator<<(ostream& out, const Complex& c) {
        out << c.real << " + " << c.imag << "i";
        return out;
    }
};

Usage:

cpp
Complex c1(2, 3), c2(2, 3);
cout << c1 << endl;

if (c1 == c2) {
    cout << "Equal\n";
}
Output2 + 3i Equal

7Custom data types: Another example – Fractions

You can define a class for fractions and overload operators like +, *, or == to make them behave naturally.

cpp
class Fraction {
public:
    int num, denom;

    Fraction(int n = 0, int d = 1) {
        num = n;
        denom = d;
    }

    Fraction operator+(const Fraction& f) {
        int n = num * f.denom + f.num * denom;
        int d = denom * f.denom;
        return Fraction(n, d);
    }

    friend ostream& operator<<(ostream& out, const Fraction& f) {
        out << f.num << "/" << f.denom;
        return out;
    }
};

Usage:

cpp
Fraction f1(1, 2);
Fraction f2(1, 3);
Fraction result = f1 + f2;
cout << result << endl;
Output5/6

8When should you overload operators?

Good reasons to overload operators
  • You are building a custom data type that represents a mathematical or logical entity
  • You want your objects to behave like built-in types
  • It makes the code more readable and natural
  • Your users expect the operator to work in a certain way
When to avoid operator overloading
  • If it makes the code confusing or less clear
  • If the operation is not well-defined or intuitive
  • If it violates the user's expectations (e.g., overloading + to subtract)

9Common mistakes to avoid

1. Forgetting to return a value from overloaded operators
Operators like + must return a new object. Forgetting to return a value causes undefined behavior.
2. Using non-const references for parameters when not needed
Use const references for parameters that are not modified. This is a good practice and enables more efficient code.
3. Not defining friend functions outside the class
The operator<< must be a non-member function (or friend) because the left operand is ostream, not your class.
4. Overcomplicating simple types
Not every class needs every operator overloaded. Only overload what makes sense and is intuitive for your type.

10Mini exercises

Try these yourself
  1. Create a Point class with x and y coordinates. Overload + to add two points.
  2. Create a Distance class in meters. Overload == and < to compare distances.
  3. Write a Fraction class and overload * and / operators.
  4. Add a friend ostream& operator<< to any class you have created to print it with cout.

Hint: For the Distance class, store the value in meters as a double and compare directly.

11Quiz yourself

Operator Overloading Quiz
Question 1 of 10
Coming up next
Final Practice & Revision
Put everything together with exercises, mini-projects, and a comprehensive review of all the concepts you have learned in this C++ series.
Continue to Chapter 13

Stop wrestling with confusion.

Join thousands of students mastering Computer Science without the academic jargon.

From syntax to systems. We break down the hardest ideas in computer science so you can actually build things.

© 2026 Painless Programming. Built for students.
Scroll to Top