04: 运算符重载 (Operator Overloading)
运算符重载让自定义类型也能用 + == << 这些操作符,代码读起来更自然。
什么是运算符重载?
运算符重载 (Operator Overloading) 允许为自定义类型赋予运算符语义:
| C++ |
|---|
| Complex a(1, 2), b(3, 4);
Complex c = a + b; // 比 Complex c = a.add(b) 更直观
|
Tip
基本原则
运算符重载的目的是让代码更自然、更易读,不是炫技。重载后应该保持运算符原有的含义。
可以重载的运算符
- 绝大多数可以重载:
+ - * / % == != < > [] () << >> 等
- 少数不能重载:
. :: ?: sizeof .* # ##
两种重载形式
| 形式 |
语法 |
适用场景 |
| 成员函数 |
T T::operator+(const T&) const |
左操作数为当前对象 |
| 非成员函数 |
T operator+(const T&, const T&) |
对称运算、左操作数非当前类 |
成员函数形式
| C++ |
|---|
| class Point {
private:
int _x, _y;
public:
Point(int x = 0, int y = 0) : _x(x), _y(y) {}
Point operator+(const Point& other) const {
return Point(_x + other._x, _y + other._y);
}
Point& operator+=(const Point& other) {
_x += other._x; _y += other._y;
return *this;
}
};
Point p1(1, 2), p2(3, 4);
Point p3 = p1 + p2; // 调用成员函数 operator+
p1 += p2; // 调用成员函数 operator+=
|
非成员函数形式
当左操作数不是当前类类型时(如 cout << point),必须使用非成员函数形式,通常设为友元。
| C++ |
|---|
| class Point {
private:
int _x, _y;
public:
Point(int x = 0, int y = 0) : _x(x), _y(y) {}
// 必须设为友元才能访问私有成员
friend ostream& operator<<(ostream& os, const Point& p);
friend istream& operator>>(istream& is, Point& p);
};
ostream& operator<<(ostream& os, const Point& p) {
os << "(" << p._x << ", " << p._y << ")";
return os; // 返回 os 以支持链式调用
}
istream& operator>>(istream& is, Point& p) {
is >> p._x >> p._y;
return is;
}
|
Important
选择标准
- 左操作数必须是当前类对象 → 成员函数
- 左操作数非当前类(如
cout)→ 非成员函数
- 对称二元运算符(如
+、==)→ 推荐非成员函数
二元算术运算符
| C++ |
|---|
| class Vector2D {
private:
double _x, _y;
public:
Vector2D(double x = 0, double y = 0) : _x(x), _y(y) {}
Vector2D operator+(const Vector2D& v) const {
return Vector2D(_x + v._x, _y + v._y);
}
Vector2D operator-(const Vector2D& v) const {
return Vector2D(_x - v._x, _y - v._y);
}
Vector2D operator*(double scalar) const {
return Vector2D(_x * scalar, _y * scalar);
}
};
|
Tip
对称性的处理
让 2 * v 也合法(左操作数是 double):
| C++ |
|---|
| Vector2D operator*(double scalar, const Vector2D& v) {
return v * scalar; // 复用成员函数版本
}
|
复合赋值运算符
通常返回引用,以支持链式赋值:
| C++ |
|---|
| class Vector2D {
double _x, _y;
public:
Vector2D& operator+=(const Vector2D& v) {
_x += v._x; _y += v._y;
return *this;
}
Vector2D& operator-=(const Vector2D& v) {
_x -= v._x; _y -= v._y;
return *this;
}
Vector2D& operator*=(double scalar) {
_x *= scalar; _y *= scalar;
return *this;
}
};
|
Tip
最佳实践
先实现 operator+= 作为成员函数,再用非成员 operator+ 复用其逻辑:
| C++ |
|---|
| Complex operator+(Complex a, const Complex& b) {
a += b;
return a;
}
|
关系运算符
| C++ |
|---|
| 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);
}
bool operator<(const Point& other) const {
if (_x != other._x) return _x < other._x;
return _y < other._y;
}
};
|
自增/自减运算符
前置与后置的区别在于参数:
| C++ |
|---|
| class Counter {
int _count = 0;
public:
// 前置 ++c
Counter& operator++() { ++_count; return *this; }
// 后置 c++(占位 int 参数)
Counter operator++(int) {
Counter temp = *this;
++_count;
return temp;
}
};
|
| 形式 |
签名 |
返回 |
效率 |
前置 ++c |
T& operator++() |
引用 |
更高 |
后置 c++ |
T operator++(int) |
值 |
需拷贝 |
Tip
优先使用前置 ++
对于自定义类型,前置 ++ 效率更高(不产生临时对象)。
输入/输出运算符 (<< >>)
左操作数是 ostream/istream,必须是非成员函数。
Important
为什么必须是非成员函数?
因为 cout << p 翻译为 operator<<(cout, p),左操作数是 ostream。
下标运算符 []
模拟数组式访问:
| C++ |
|---|
| class IntArray {
int _data[10];
public:
// 非 const 版本:可读写
int& operator[](int index) {
if (index < 0 || index >= 10) throw out_of_range("out of bounds");
return _data[index];
}
// const 版本:只读
const int& operator[](int index) const {
if (index < 0 || index >= 10) throw out_of_range("out of bounds");
return _data[index];
}
};
|
函数调用运算符 ()(Functor)
允许对象像函数一样调用:
| C++ |
|---|
| class Adder {
int _value;
public:
Adder(int v) : _value(v) {}
int operator()(int x) const { return _value + x; }
};
Adder add5(5);
cout << add5(10) << endl; // 输出 15
// 标准库中的函数对象
sort(vec.begin(), vec.end(), less<int>());
|
类型转换运算符
定义从自定义类型到其他类型的转换:
| C++ |
|---|
| class Fraction {
int _num, _den;
public:
Fraction(int n, int d = 1) : _num(n), _den(d) {}
operator double() const {
return static_cast<double>(_num) / _den;
}
};
Fraction f(3, 4);
double d = f; // 隐式调用 operator double()
|
Warning
隐式转换需谨慎
C++11 中可使用 explicit 限制:
| C++ |
|---|
| explicit operator double() const; // 只能显式转换
|
最佳实践总结
| 准则 |
说明 |
| 保持语义一致 |
+ 不能做减法,== 不能做排序 |
| 尊重优先级 |
无法改变运算符优先级 |
不重载 && \|\| , |
会失去短路求值特性 |
| 提供对称性 |
重载 + 通常也要重载 += |
| 复合赋值 + 非成员 |
先实现 += 为成员,再用非成员 + 复用 |