Operator Overloading in C++

    Atul Kabra4 min readUpdated

    Operator overloading lets you define what built-in operators like +, ==, or << mean for your own types. Once you teach a Vector2D how to add, you can write a + b instead of a.add(b). Used well, it makes your types feel like first-class citizens of the language. Used carelessly, it surprises readers — so the golden rule is make operators do the obvious thing.

    Let us overload a few.

    Overloading + as a member function

    A binary operator written as a member takes the left operand as this and the right operand as a parameter.

    #include <iostream>
    
    class Vector2D {
    public:
        double x = 0, y = 0;
    
        Vector2D(double x_, double y_) : x(x_), y(y_) {}
    
        // a + b  ->  a.operator+(b)
        Vector2D operator+(const Vector2D& other) const {
            return Vector2D{x + other.x, y + other.y};
        }
    };
    
    int main() {
        Vector2D a{1, 2}, b{3, 4};
        Vector2D c = a + b;          // uses operator+
        std::cout << c.x << ", " << c.y << "\n";  // 4, 6
    }
    

    Overloading << for printing (a friend function)

    To print with std::cout << v, the left operand is the stream, not your object — so this operator must be a free function. Declaring it a friend lets it read private members.

    #include <iostream>
    
    class Vector2D {
        double x, y;
    public:
        Vector2D(double x_, double y_) : x(x_), y(y_) {}
    
        // grant the operator access to private x, y
        friend std::ostream& operator<<(std::ostream& os, const Vector2D& v) {
            os << "(" << v.x << ", " << v.y << ")";
            return os;   // return the stream so calls can chain
        }
    };
    
    int main() {
        Vector2D v{4, 6};
        std::cout << "v = " << v << "\n";   // v = (4, 6)
    }
    

    Returning the stream by reference is what lets you chain std::cout << a << b.

    Want to learn this properly?

    Join the waitlist for our courses — beginner-friendly, project-first classes in Jalgaon.

    Browse courses

    Overloading comparison operators

    Equality is one of the most common overloads. In modern C++ you can let the compiler generate it.

    #include <iostream>
    
    struct Point {
        int x, y;
        // C++20: compiler generates ==, != automatically
        bool operator==(const Point&) const = default;
    };
    
    int main() {
        Point p{1, 2}, q{1, 2};
        std::cout << std::boolalpha << (p == q) << "\n"; // true
    }
    

    For ordering, auto operator<=>(const Point&) const = default; (the C++20 "spaceship" operator) generates <, <=, >, and >= in one line. Letting the compiler generate these means they stay correct even when you add or reorder members later — a hand-written comparison that forgets a new field is a classic, silent bug.

    Compound assignment and chaining

    A common pattern is to implement the compound form (+=) as a member that returns *this, then define + in terms of it. This keeps the logic in one place and makes both operators consistent.

    class Money {
        long paise = 0;
    public:
        Money& operator+=(const Money& rhs) {
            paise += rhs.paise;
            return *this;            // return self so a += b += c works
        }
        friend Money operator+(Money lhs, const Money& rhs) {
            lhs += rhs;              // reuse += ; lhs is a copy we can modify
            return lhs;
        }
    };
    

    Taking lhs by value and returning it is the idiomatic way to write a binary + on top of +=.

    Design rules

    • Keep meaning intuitive. + should combine, == should compare. Never overload + to mean subtraction.
    • Members for operators that modify the left operand (+=, ++); free/friend functions for symmetric ones (+, ==, <<).
    • Return by value for arithmetic, by reference for assignment (operator= returns *this).
    • Prefer compiler-generated comparisons (= default) — they are correct and complete.

    Common mistakes

    • Overloading operators with surprising meaning. If a * b does something unexpected, every reader is fooled. Match built-in intuition.
    • Forgetting to return the stream from <<. Without return os;, chained output stops compiling.
    • Making operator<< a member. The left operand is the stream, so it must be a non-member (often a friend).
    • Returning a reference to a local. Arithmetic operators create a new result; return it by value, not as a dangling reference.

    FAQ

    Which operators can I overload? Most of them — arithmetic, comparison, subscript [], call (), stream <</>>, and more. A few like ::, ., and ?: cannot be overloaded.

    Should I always overload operators for my classes? Only when an operator has an obvious, conventional meaning for your type (numbers, points, money). If the meaning is not instantly clear, a named function is friendlier.

    Keep learning

    Write expressive, idiomatic C++ with mentor feedback — join the waitlist for our C++ Programming course in Jalgaon.

    Want to learn this properly?

    Join the waitlist for our courses — beginner-friendly, project-first classes in Jalgaon.

    Browse courses
    Atul Kabra

    Founder, Infoplanet

    Atul Kabra founded Infoplanet in 2001 and has spent over two decades teaching programming — C, C++, Java, databases and more — to students across Maharashtra.

    Related guides