C++ Operator Overloading

Customizing operator behavior for user-defined types

⚙️ What is Operator Overloading?

Operator overloading allows you to redefine the behavior of operators for user-defined classes, making objects work with standard operators like +, -, *, etc.


// Simple operator overloading example
class Point {
public:
    int x, y;
    Point(int x = 0, int y = 0) : x(x), y(y) {}
    
    Point operator+(const Point& other) {
        return Point(x + other.x, y + other.y);
    }
};
                                    

Usage:

Point p1(3, 4);
Point p2(1, 2);
Point p3 = p1 + p2; // (4, 6)

Types of Operator Overloading

Arithmetic

+, -, *, /, % operators

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

Assignment

=, +=, -=, *=, /= operators

Point& operator=(const Point& other) {
    x = other.x; y = other.y;
    return *this;
}
🔍

Comparison

==, !=, <, >, <=, >= operators

bool operator==(const Point& other) {
    return x == other.x && y == other.y;
}
📤

Stream

<<, >> operators for I/O

friend ostream& operator<<(ostream& os, const Point& p) {
    return os << "(" << p.x << ", " << p.y << ")";
}

🔹 Complete Example: Complex Number Class

A comprehensive Complex Number class in C++ demonstrates operator overloading for intuitive mathematical operations. By overloading operators like + and *, you enable natural syntax such as c1 + c2 and c1 * c2 for complex arithmetic. The class typically includes private real and imaginary members, constructors, and friend functions for output. This encapsulation allows you to perform calculations like (3 + 4i) * (1 + 2i) = -5 + 10i directly, making the code more readable and closely mirroring mathematical notation while ensuring proper memory management.

class Complex {
private:
    double real, imag;

public:
    Complex(double r = 0, double i = 0) : real(r), imag(i) {}
    
    // Arithmetic operators
    Complex operator+(const Complex& c) const {
        return Complex(real + c.real, imag + c.imag);
    }
    
    Complex operator-(const Complex& c) const {
        return Complex(real - c.real, imag - c.imag);
    }
    
    Complex operator*(const Complex& c) const {
        return Complex(real * c.real - imag * c.imag,
                      real * c.imag + imag * c.real);
    }
    
    // Assignment operator
    Complex& operator=(const Complex& c) {
        real = c.real;
        imag = c.imag;
        return *this;
    }
    
    // Comparison operator
    bool operator==(const Complex& c) const {
        return (real == c.real) && (imag == c.imag);
    }
    
    // Stream insertion operator (friend function)
    friend ostream& operator<<(ostream& os, const Complex& c) {
        os << c.real;
        if (c.imag >= 0) os << " + " << c.imag << "i";
        else os << " - " << -c.imag << "i";
        return os;
    }
    
    // Stream extraction operator (friend function)
    friend istream& operator>>(istream& is, Complex& c) {
        cout << "Enter real part: ";
        is >> c.real;
        cout << "Enter imaginary part: ";
        is >> c.imag;
        return is;
    }
};

int main() {
    Complex c1(3, 4);
    Complex c2(1, 2);
    
    Complex c3 = c1 + c2;
    Complex c4 = c1 * c2;
    
    cout << "c1 = " << c1 << endl;
    cout << "c2 = " << c2 << endl;
    cout << "c1 + c2 = " << c3 << endl;
    cout << "c1 * c2 = " << c4 << endl;
    
    return 0;
}

Output:

c1 = 3 + 4i
c2 = 1 + 2i
c1 + c2 = 4 + 6i
c1 * c2 = -5 + 10i

🔹 Unary Operator Overloading

Overloading unary operators like ++, --, and - allows objects to behave like built-in types. You can define both prefix (e.g., ++obj) and postfix (e.g., obj++) versions by using an int dummy parameter for postfix. For instance, a Counter class might increment or decrement an internal count, returning the appropriate value or reference. This enables intuitive operations such as Count: 6 becoming Count: 7 after incrementing, making user-defined types more expressive and integrated with C++'s idiomatic patterns.

class Counter {
private:
    int count;

public:
    Counter(int c = 0) : count(c) {}
    
    // Pre-increment operator
    Counter& operator++() {
        ++count;
        return *this;
    }
    
    // Post-increment operator
    Counter operator++(int) {
        Counter temp = *this;
        count++;
        return temp;
    }
    
    // Unary minus operator
    Counter operator-() const {
        return Counter(-count);
    }
    
    void display() const {
        cout << "Count: " << count << endl;
    }
};

int main() {
    Counter c1(5);
    
    ++c1;           // Pre-increment
    c1.display();   // Count: 6
    
    Counter c2 = c1++;  // Post-increment
    c1.display();   // Count: 7
    c2.display();   // Count: 6
    
    Counter c3 = -c1;   // Unary minus
    c3.display();   // Count: -7
    
    return 0;
}

Output:

Count: 6
Count: 7
Count: 6
Count: -7

🧠 Test Your Knowledge

Which operators cannot be overloaded in C++?