Skillversitas

Operator Overloading in C++

Operator overloading is a C++ feature that allows programmers to redefine the behavior of operators for custom data types. With operator overloading, class objects can be operated on with operators like +, -, *, ==, <<, >>, and others, intuitively, just like built-in data types.

Operator overloading can be done as a class member function or as a friend function. Some operators such as =, [], (), and -> must be overloaded as class member functions.

Code Example

#include <iostream>
#include <vector>
using namespace std;

class Matrix {
private:
    int rows, cols;
    vector<vector<double>> data;

public:
    Matrix(int r = 0, int c = 0) : rows(r), cols(c) {
        data.resize(r, vector<double>(c, 0));
    }

    void input() {
        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < cols; j++) {
                cin >> data[i][j];
            }
        }
    }

    Matrix operator+(const Matrix& other) {
        Matrix result(rows, cols);
        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < cols; j++) {
                result.data[i][j] = data[i][j] + other.data[i][j];
            }
        }
        return result;
    }

    Matrix operator-(const Matrix& other) {
        Matrix result(rows, cols);
        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < cols; j++) {
                result.data[i][j] = data[i][j] - other.data[i][j];
            }
        }
        return result;
    }

    Matrix operator*(const Matrix& other) {
        Matrix result(rows, other.cols);
        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < other.cols; j++) {
                for (int k = 0; k < cols; k++) {
                    result.data[i][j] += data[i][k] * other.data[k][j];
                }
            }
        }
        return result;
    }

    Matrix operator*(double scalar) {
        Matrix result(rows, cols);
        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < cols; j++) {
                result.data[i][j] = data[i][j] * scalar;
            }
        }
        return result;
    }

    bool operator==(const Matrix& other) {
        if (rows != other.rows || cols != other.cols) return false;
        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < cols; j++) {
                if (data[i][j] != other.data[i][j]) return false;
            }
        }
        return true;
    }

    bool operator!=(const Matrix& other) {
        return !(*this == other);
    }

    double& operator()(int r, int c) {
        return data[r][c];
    }

    Matrix& operator+=(const Matrix& other) {
        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < cols; j++) {
                data[i][j] += other.data[i][j];
            }
        }
        return *this;
    }

    Matrix operator-() {
        Matrix result(rows, cols);
        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < cols; j++) {
                result.data[i][j] = -data[i][j];
            }
        }
        return result;
    }

    friend ostream& operator<<(ostream& os, const Matrix& m);
    friend istream& operator>>(istream& is, Matrix& m);
    friend Matrix operator*(double scalar, const Matrix& m);
};

ostream& operator<<(ostream& os, const Matrix& m) {
    for (int i = 0; i < m.rows; i++) {
        for (int j = 0; j < m.cols; j++) {
            os << m.data[i][j] << "\t";
        }
        os << endl;
    }
    return os;
}

istream& operator>>(istream& is, Matrix& m) {
    for (int i = 0; i < m.rows; i++) {
        for (int j = 0; j < m.cols; j++) {
            is >> m.data[i][j];
        }
    }
    return is;
}

Matrix operator*(double scalar, const Matrix& m) {
    Matrix result(m.rows, m.cols);
    for (int i = 0; i < m.rows; i++) {
        for (int j = 0; j < m.cols; j++) {
            result.data[i][j] = m.data[i][j] * scalar;
        }
    }
    return result;
}

int main() {
    cout << "=== Input Matrix ===" << endl;
    Matrix A(2, 2);
    Matrix B(2, 2);

    cout << "Enter matrix A (2x2):" << endl;
    A(0,0) = 1; A(0,1) = 2; A(1,0) = 3; A(1,1) = 4;

    cout << "Enter matrix B (2x2):" << endl;
    B(0,0) = 5; B(0,1) = 6; B(1,0) = 7; B(1,1) = 8;

    cout << "Matrix A:" << endl << A;
    cout << "Matrix B:" << endl << B;

    cout << "\n=== Operator + ===" << endl;
    Matrix C = A + B;
    cout << "A + B:" << endl << C;

    cout << "\n=== Operator - ===" << endl;
    Matrix D = A - B;
    cout << "A - B:" << endl << D;

    cout << "\n=== Operator * (matrix) ===" << endl;
    Matrix E = A * B;
    cout << "A * B:" << endl << E;

    cout << "\n=== Operator * (scalar) ===" << endl;
    Matrix F = A * 2.0;
    cout << "A * 2:" << endl << F;

    cout << "2 * A:" << endl << 2.0 * A;

    cout << "\n=== Operator == and != ===" << endl;
    cout << "A == B: " << (A == B ? "true" : "false") << endl;
    cout << "A != B: " << (A != B ? "true" : "false") << endl;

    cout << "\n=== Operator () ===" << endl;
    cout << "A(0,0) = " << A(0,0) << endl;
    cout << "A(1,1) = " << A(1,1) << endl;

    cout << "\n=== Operator += ===" << endl;
    A += B;
    cout << "A += B:" << endl << A;

    cout << "\n=== Operator unary - ===" << endl;
    Matrix G = -B;
    cout << "-B:" << endl << G;

    return 0;
}

Code Explanation

  1. Matrix operator+(const Matrix& other) - Overloads the + operator as a member function. The parameter other is the right operand. Prefix unary operators like + only need one parameter.

  2. Matrix operator*(double scalar) - Overloads the * operator for matrix-scalar multiplication. Different from operator*(const Matrix& other), which multiplies two matrices.

  3. bool operator==(const Matrix& other) - Overloads a comparison operator. Returns true if all elements are exactly the same.

  4. double& operator()(int r, int c) - Overloads the () operator for accessing matrix elements. Returns a reference so the element can be assigned to.

  5. Matrix& operator+=(const Matrix& other) - A compound assignment operator. Modifies the matrix itself and returns a reference to *this.

  6. Matrix operator-() - A unary minus operator. Creates a new matrix with all elements negated.

  7. friend ostream& operator<<(ostream& os, const Matrix& m) - Overloads the << operator for the output stream. It must be a friend because the ostream is on the left side.

  8. Matrix operator*(double scalar, const Matrix& m) - A friend function for the operation 2.0 * A. The scalar is on the left, the matrix on the right.

  9. A(0,0) = 1 - Uses the overloaded () operator to access and modify matrix elements.

Everyday Use Cases

Imagine a scientific calculator. A calculator can add, subtract, and multiply ordinary numbers. But with special programming, the calculator can also add matrices, vectors, or complex numbers. Operator overloading is like programming the calculator to understand how to operate on new data types.

Conclusion

Operator overloading makes C++ operators work with custom data types. Some operators must be overloaded as member functions: =, [], (), ->. Other operators can be either member functions or friends. Stream operators (<<, >>) must be friends because the stream object is on the left side of the operator. Left-side unary operators (++x) have different parameters than right-side ones (x++). Use overloading wisely so the code stays intuitive and easy to read.