cpp

operator overloading

01_operator_overloading.cpp⚙️
/**
 * ============================================================
 * C++ OPERATOR OVERLOADING
 * ============================================================
 * 
 * This file covers:
 * - What is operator overloading
 * - Overloading arithmetic operators
 * - Overloading comparison operators
 * - Overloading stream operators
 * - Overloading assignment and subscript
 * - Friend functions
 * 
 * Compile: g++ -std=c++17 -Wall 01_operator_overloading.cpp -o op_overload
 * Run: ./op_overload
 * 
 * ============================================================
 */

#include <iostream>
#include <string>
#include <cmath>
#include <stdexcept>

using namespace std;

// ============================================================
// EXAMPLE: COMPLEX NUMBER CLASS
// ============================================================

class Complex {
private:
    double real;
    double imag;
    
public:
    // Constructors
    Complex(double r = 0, double i = 0) : real(r), imag(i) {}
    
    // Getters
    double getReal() const { return real; }
    double getImag() const { return imag; }
    
    // Arithmetic operators (member functions)
    Complex operator+(const Complex& other) const {
        return Complex(real + other.real, imag + other.imag);
    }
    
    Complex operator-(const Complex& other) const {
        return Complex(real - other.real, imag - other.imag);
    }
    
    Complex operator*(const Complex& other) const {
        // (a + bi)(c + di) = (ac - bd) + (ad + bc)i
        return Complex(
            real * other.real - imag * other.imag,
            real * other.imag + imag * other.real
        );
    }
    
    Complex operator/(const Complex& other) const {
        double denom = other.real * other.real + other.imag * other.imag;
        return Complex(
            (real * other.real + imag * other.imag) / denom,
            (imag * other.real - real * other.imag) / denom
        );
    }
    
    // Unary minus
    Complex operator-() const {
        return Complex(-real, -imag);
    }
    
    // Compound assignment operators
    Complex& operator+=(const Complex& other) {
        real += other.real;
        imag += other.imag;
        return *this;
    }
    
    Complex& operator-=(const Complex& other) {
        real -= other.real;
        imag -= other.imag;
        return *this;
    }
    
    // Comparison operators
    bool operator==(const Complex& other) const {
        return real == other.real && imag == other.imag;
    }
    
    bool operator!=(const Complex& other) const {
        return !(*this == other);
    }
    
    // Magnitude for comparison
    double magnitude() const {
        return sqrt(real * real + imag * imag);
    }
    
    bool operator<(const Complex& other) const {
        return magnitude() < other.magnitude();
    }
    
    // Stream operators (friend functions)
    friend ostream& operator<<(ostream& os, const Complex& c);
    friend istream& operator>>(istream& is, Complex& c);
};

// Stream output (non-member friend)
ostream& operator<<(ostream& os, const Complex& c) {
    os << c.real;
    if (c.imag >= 0) os << "+";
    os << c.imag << "i";
    return os;
}

// Stream input (non-member friend)
istream& operator>>(istream& is, Complex& c) {
    cout << "Enter real part: ";
    is >> c.real;
    cout << "Enter imaginary part: ";
    is >> c.imag;
    return is;
}

// ============================================================
// EXAMPLE: VECTOR2D CLASS
// ============================================================

class Vector2D {
private:
    double x, y;
    
public:
    Vector2D(double x = 0, double y = 0) : x(x), y(y) {}
    
    // Getters
    double getX() const { return x; }
    double getY() const { return y; }
    
    // Addition
    Vector2D operator+(const Vector2D& v) const {
        return Vector2D(x + v.x, y + v.y);
    }
    
    // Subtraction
    Vector2D operator-(const Vector2D& v) const {
        return Vector2D(x - v.x, y - v.y);
    }
    
    // Scalar multiplication (Vector * scalar)
    Vector2D operator*(double scalar) const {
        return Vector2D(x * scalar, y * scalar);
    }
    
    // Dot product (Vector * Vector)
    double operator*(const Vector2D& v) const {
        return x * v.x + y * v.y;
    }
    
    // Unary minus
    Vector2D operator-() const {
        return Vector2D(-x, -y);
    }
    
    // Compound assignment
    Vector2D& operator+=(const Vector2D& v) {
        x += v.x;
        y += v.y;
        return *this;
    }
    
    // Subscript operator
    double& operator[](int index) {
        if (index == 0) return x;
        if (index == 1) return y;
        throw out_of_range("Index must be 0 or 1");
    }
    
    const double& operator[](int index) const {
        if (index == 0) return x;
        if (index == 1) return y;
        throw out_of_range("Index must be 0 or 1");
    }
    
    // Magnitude
    double magnitude() const {
        return sqrt(x * x + y * y);
    }
    
    // Comparison (by magnitude)
    bool operator==(const Vector2D& v) const {
        return x == v.x && y == v.y;
    }
    
    bool operator<(const Vector2D& v) const {
        return magnitude() < v.magnitude();
    }
    
    // Friend for scalar * Vector
    friend Vector2D operator*(double scalar, const Vector2D& v);
    friend ostream& operator<<(ostream& os, const Vector2D& v);
};

// Scalar * Vector (non-member)
Vector2D operator*(double scalar, const Vector2D& v) {
    return Vector2D(v.x * scalar, v.y * scalar);
}

// Stream output
ostream& operator<<(ostream& os, const Vector2D& v) {
    os << "(" << v.x << ", " << v.y << ")";
    return os;
}

// ============================================================
// EXAMPLE: DYNAMIC ARRAY CLASS
// ============================================================

class DynamicArray {
private:
    int* data;
    size_t size;
    size_t capacity;
    
    void resize(size_t newCapacity) {
        int* newData = new int[newCapacity];
        for (size_t i = 0; i < size; i++) {
            newData[i] = data[i];
        }
        delete[] data;
        data = newData;
        capacity = newCapacity;
    }
    
public:
    // Constructor
    DynamicArray(size_t initialCapacity = 10)
        : data(new int[initialCapacity]), size(0), capacity(initialCapacity) {}
    
    // Copy constructor
    DynamicArray(const DynamicArray& other)
        : data(new int[other.capacity]), size(other.size), capacity(other.capacity) {
        for (size_t i = 0; i < size; i++) {
            data[i] = other.data[i];
        }
        cout << "  Copy constructor called" << endl;
    }
    
    // Move constructor
    DynamicArray(DynamicArray&& other) noexcept
        : data(other.data), size(other.size), capacity(other.capacity) {
        other.data = nullptr;
        other.size = 0;
        other.capacity = 0;
        cout << "  Move constructor called" << endl;
    }
    
    // Destructor
    ~DynamicArray() {
        delete[] data;
    }
    
    // Copy assignment operator
    DynamicArray& operator=(const DynamicArray& other) {
        if (this != &other) {
            delete[] data;
            size = other.size;
            capacity = other.capacity;
            data = new int[capacity];
            for (size_t i = 0; i < size; i++) {
                data[i] = other.data[i];
            }
        }
        cout << "  Copy assignment called" << endl;
        return *this;
    }
    
    // Move assignment operator
    DynamicArray& operator=(DynamicArray&& other) noexcept {
        if (this != &other) {
            delete[] data;
            data = other.data;
            size = other.size;
            capacity = other.capacity;
            other.data = nullptr;
            other.size = 0;
            other.capacity = 0;
        }
        cout << "  Move assignment called" << endl;
        return *this;
    }
    
    // Subscript operator
    int& operator[](size_t index) {
        if (index >= size) throw out_of_range("Index out of bounds");
        return data[index];
    }
    
    const int& operator[](size_t index) const {
        if (index >= size) throw out_of_range("Index out of bounds");
        return data[index];
    }
    
    // Append using +=
    DynamicArray& operator+=(int value) {
        if (size >= capacity) {
            resize(capacity * 2);
        }
        data[size++] = value;
        return *this;
    }
    
    // Concatenate using +
    DynamicArray operator+(const DynamicArray& other) const {
        DynamicArray result(size + other.size);
        for (size_t i = 0; i < size; i++) {
            result += data[i];
        }
        for (size_t i = 0; i < other.size; i++) {
            result += other.data[i];
        }
        return result;
    }
    
    // Comparison
    bool operator==(const DynamicArray& other) const {
        if (size != other.size) return false;
        for (size_t i = 0; i < size; i++) {
            if (data[i] != other.data[i]) return false;
        }
        return true;
    }
    
    // Size getter
    size_t getSize() const { return size; }
    
    // Stream output
    friend ostream& operator<<(ostream& os, const DynamicArray& arr);
};

ostream& operator<<(ostream& os, const DynamicArray& arr) {
    os << "[";
    for (size_t i = 0; i < arr.size; i++) {
        os << arr.data[i];
        if (i < arr.size - 1) os << ", ";
    }
    os << "]";
    return os;
}

// ============================================================
// MAIN FUNCTION
// ============================================================

int main() {
    cout << "============================================" << endl;
    cout << "     C++ OPERATOR OVERLOADING" << endl;
    cout << "============================================" << endl << endl;

    // ========================================================
    // DEMO 1: Complex Numbers
    // ========================================================
    
    cout << "--- DEMO 1: COMPLEX NUMBERS ---" << endl << endl;
    
    Complex c1(3, 4);
    Complex c2(1, 2);
    
    cout << "c1 = " << c1 << endl;
    cout << "c2 = " << c2 << endl;
    
    cout << "\nArithmetic operations:" << endl;
    cout << "c1 + c2 = " << (c1 + c2) << endl;
    cout << "c1 - c2 = " << (c1 - c2) << endl;
    cout << "c1 * c2 = " << (c1 * c2) << endl;
    cout << "c1 / c2 = " << (c1 / c2) << endl;
    cout << "-c1 = " << (-c1) << endl;
    
    cout << "\nCompound assignment:" << endl;
    Complex c3 = c1;
    c3 += c2;
    cout << "c3 = c1; c3 += c2; c3 = " << c3 << endl;
    
    cout << "\nComparison:" << endl;
    cout << "c1 == c2: " << (c1 == c2 ? "true" : "false") << endl;
    cout << "c1 != c2: " << (c1 != c2 ? "true" : "false") << endl;
    cout << "|c1| = " << c1.magnitude() << ", |c2| = " << c2.magnitude() << endl;
    cout << "c1 < c2: " << (c1 < c2 ? "true" : "false") << endl;
    
    cout << endl;

    // ========================================================
    // DEMO 2: Vector2D
    // ========================================================
    
    cout << "--- DEMO 2: VECTOR2D ---" << endl << endl;
    
    Vector2D v1(3, 4);
    Vector2D v2(1, 2);
    
    cout << "v1 = " << v1 << endl;
    cout << "v2 = " << v2 << endl;
    
    cout << "\nVector operations:" << endl;
    cout << "v1 + v2 = " << (v1 + v2) << endl;
    cout << "v1 - v2 = " << (v1 - v2) << endl;
    cout << "v1 * 2 = " << (v1 * 2) << endl;
    cout << "3 * v2 = " << (3 * v2) << endl;
    cout << "v1 dot v2 = " << (v1 * v2) << endl;
    cout << "-v1 = " << (-v1) << endl;
    cout << "|v1| = " << v1.magnitude() << endl;
    
    cout << "\nSubscript operator:" << endl;
    cout << "v1[0] = " << v1[0] << ", v1[1] = " << v1[1] << endl;
    
    cout << endl;

    // ========================================================
    // DEMO 3: Dynamic Array
    // ========================================================
    
    cout << "--- DEMO 3: DYNAMIC ARRAY ---" << endl << endl;
    
    cout << "Creating and filling arr1:" << endl;
    DynamicArray arr1;
    arr1 += 10;
    arr1 += 20;
    arr1 += 30;
    cout << "arr1 = " << arr1 << endl;
    
    cout << "\nCopying arr1 to arr2:" << endl;
    DynamicArray arr2 = arr1;
    cout << "arr2 = " << arr2 << endl;
    
    cout << "\nModifying arr2:" << endl;
    arr2 += 40;
    arr2[0] = 100;
    cout << "arr1 = " << arr1 << " (unchanged)" << endl;
    cout << "arr2 = " << arr2 << " (modified)" << endl;
    
    cout << "\nConcatenation:" << endl;
    DynamicArray arr3 = arr1 + arr2;
    cout << "arr1 + arr2 = " << arr3 << endl;
    
    cout << "\nComparison:" << endl;
    cout << "arr1 == arr2: " << (arr1 == arr2 ? "true" : "false") << endl;
    
    cout << "\nMove semantics:" << endl;
    DynamicArray arr4 = move(arr3);
    cout << "arr4 (after move) = " << arr4 << endl;
    
    cout << endl;

    // ========================================================
    // OPERATOR OVERLOADING SUMMARY
    // ========================================================
    
    cout << "--- OPERATOR OVERLOADING SUMMARY ---" << endl << endl;
    
    cout << "Operators that CAN be overloaded:" << endl;
    cout << "─────────────────────────────────────────" << endl;
    cout << "+ - * / % ^ & | ~" << endl;
    cout << "! = < > += -= *= /= %= ^= &= |=" << endl;
    cout << "<< >> >>= <<= == != <= >=" << endl;
    cout << "++ -- , ->* -> () []" << endl;
    cout << "new delete new[] delete[]" << endl;
    
    cout << "\nOperators that CANNOT be overloaded:" << endl;
    cout << "─────────────────────────────────────────" << endl;
    cout << ".  (member access)" << endl;
    cout << ":: (scope resolution)" << endl;
    cout << "?: (ternary)" << endl;
    cout << "sizeof" << endl;
    
    cout << "\nBest Practices:" << endl;
    cout << "─────────────────────────────────────────" << endl;
    cout << "• Maintain expected semantics" << endl;
    cout << "• Return reference for = += -= etc." << endl;
    cout << "• Return new object for + - * /" << endl;
    cout << "• Implement == and != together" << endl;
    cout << "• Use friend for symmetric operators" << endl;
    cout << "• Make << >> friends for streams" << endl;
    
    cout << endl;

    cout << "============================================" << endl;
    cout << "OPERATOR OVERLOADING DONE!" << endl;
    cout << "============================================" << endl;

    return 0;
}

// ============================================================
// EXERCISES:
// ============================================================
/*
 * 1. Create a Fraction class with:
 *    - +, -, *, / operators
 *    - Comparison operators
 *    - Stream I/O
 *    - Automatic reduction to lowest terms
 * 
 * 2. Create a Matrix class with:
 *    - Matrix addition and subtraction
 *    - Matrix multiplication
 *    - Scalar multiplication
 *    - Subscript operator (row,col)
 *    - Stream output
 * 
 * 3. Create a String class with:
 *    - + for concatenation
 *    - [] for character access
 *    - Comparison operators
 *    - Stream I/O
 *    - Implement Rule of Five
 * 
 * 4. Create a BigInteger class with:
 *    - Arbitrary precision arithmetic
 *    - All arithmetic operators
 *    - Comparison operators
 */
Operator Overloading - C++ Tutorial | DeepML