Search Knowledge

© 2026 LIBREUNI PROJECT

Modern C++ Programming / Object-Oriented Programming

Operator Overloading

Operator Overloading: Custom Type Semantics

C++ allows redefining operators for user-defined types, enabling natural syntax for custom abstractions. Operators are functions with special names: operator+, operator[], operator<<, etc.

class Complex {
    double real_, imag_;
public:
    Complex(double r = 0, double i = 0) : real_(r), imag_(i) {}
    
    // Member operator
    Complex operator+(const Complex& other) const {
        return Complex(real_ + other.real_, imag_ + other.imag_);
    }
    
    Complex& operator+=(const Complex& other) {
        real_ += other.real_;
        imag_ += other.imag_;
        return *this;
    }
};

Complex a(1, 2), b(3, 4);
Complex c = a + b;  // Calls a.operator+(b)

Member vs Non-Member Operators

Member operators: Left operand is *this

class Vector {
public:
    Vector operator+(const Vector& rhs) const;  // this + rhs
};

Non-member operators: Both operands are parameters

Vector operator+(const Vector& lhs, const Vector& rhs);

When to use each:

  • Member: Unary operators (-, ++, *), compound assignment (+=, *=), subscript ([]), call (()), member access (->)
  • Non-member: Symmetric binary operators (+, -, *), stream operators (<<, >>)
  • Friend: Non-member needing private access
class Rational {
    int num_, den_;
    friend Rational operator+(const Rational& lhs, const Rational& rhs);
public:
    Rational(int n, int d = 1) : num_(n), den_(d) {}
};

// Non-member allows: 2 + Rational(3,4) via implicit conversion
Rational operator+(const Rational& lhs, const Rational& rhs) {
    return Rational(lhs.num_ * rhs.den_ + rhs.num_ * lhs.den_,
                    lhs.den_ * rhs.den_);
}

Canonical Operator Patterns

Arithmetic Operators

Implement compound assignment (+=), then derive binary operator (+):

class Number {
    int value_;
public:
    Number& operator+=(const Number& rhs) {
        value_ += rhs.value_;
        return *this;
    }
};

// Non-member, defined in terms of +=
Number operator+(Number lhs, const Number& rhs) {
    lhs += rhs;  // lhs is a copy, so modify it
    return lhs;
}

Comparison Operators

C++20 introduced the spaceship operator (<=>), but pre-C++20:

class Point {
    int x_, y_;
public:
    bool operator==(const Point& other) const {
        return x_ == other.x_ && y_ == other.y_;
    }
    
    bool operator!=(const Point& other) const {
        return !(*this == other);  // Implement in terms of ==
    }
    
    bool operator<(const Point& other) const {
        return (x_ < other.x_) || (x_ == other.x_ && y_ < other.y_);
    }
    
    // Define >, <=, >= in terms of < and ==
};

C++20 spaceship simplifies this:

class Point {
    int x_, y_;
public:
    auto operator<=>(const Point&) const = default;  // Generates all comparisons
};

Increment/Decrement

Prefix returns reference; postfix returns copy:

class Counter {
    int count_;
public:
    // Prefix: ++c
    Counter& operator++() {
        ++count_;
        return *this;
    }
    
    // Postfix: c++
    Counter operator++(int) {  // Dummy int parameter
        Counter temp = *this;
        ++(*this);  // Use prefix version
        return temp;
    }
};

Subscript Operator

class Array {
    int* data_;
    size_t size_;
public:
    int& operator[](size_t index) {
        return data_[index];
    }
    
    const int& operator[](size_t index) const {
        return data_[index];
    }
};

C++23 allows multidimensional subscript:

class Matrix {
public:
    double& operator[](size_t row, size_t col);  // C++23
};

Stream Insertion/Extraction

Always non-member to allow std::cout << obj:

class Person {
    std::string name_;
    int age_;
    friend std::ostream& operator<<(std::ostream&, const Person&);
public:
    Person(std::string n, int a) : name_(n), age_(a) {}
};

std::ostream& operator<<(std::ostream& os, const Person& p) {
    return os << p.name_ << " (" << p.age_ << ")";
}

Conversion Operators

Allow implicit or explicit conversion to other types:

class Fraction {
    int num_, den_;
public:
    // Implicit conversion to double
    operator double() const {
        return static_cast<double>(num_) / den_;
    }
    
    // Explicit conversion to bool (C++11)
    explicit operator bool() const {
        return num_ != 0;
    }
};

Fraction f(3, 4);
double d = f;  // Implicit: calls operator double()
// bool b = f;  // Error: explicit conversion required
bool b = static_cast<bool>(f);  // OK
if (f) { }  // OK: contextual conversion to bool
Conceptual Check

Why implement operator+ as non-member in terms of operator+=?

Runtime Environment

Interactive Lab

1#include <iostream>
2 
3class Vector2D {
4 double x_, y_;
5public:
6 Vector2D(double x, double y) : x_(x), y_(y) {}
7
8 Vector2D& operator+=(const Vector2D& rhs) {
9 x_ += rhs.x_;
10 y_ += rhs.y_;
11 return *this;
12 }
13
14 friend Vector2D operator+(Vector2D lhs, const Vector2D& rhs) {
15 return lhs += rhs;
16 }
17
18 friend std::ostream& operator<<(std::ostream& os, const Vector2D& v) {
19 return os << "(" << v.x_ << ", " << v.y_ << ")";
20 }
21};
22 
23int main() {
24 Vector2D v1(1, 2), v2(3, 4);
25 std::cout << v1 + v2 << '\n';
26 return 0;
27}
System Console

Waiting for signal...