cpp
Operator Overloading
05_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
*/