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...