8.1 重载的基本规则
什么是运算符重载
Section titled “什么是运算符重载”运算符重载本质上是定义一个名为 operator符号 的函数。例如,重载 + 运算符就是定义一个名为 operator+ 的函数:
#include <iostream>
class Vector2D {public: double x, y;
Vector2D(double x = 0, double y = 0) : x(x), y(y) {}
// 重载 + 运算符 Vector2D operator+(const Vector2D& other) const { return Vector2D(x + other.x, y + other.y); }};
int main() { Vector2D a(1.0, 2.0); Vector2D b(3.0, 4.0);
Vector2D c = a + b; // 等价于 a.operator+(b) std::cout << "(" << c.x << ", " << c.y << ")" << std::endl; // (4, 6)
return 0;}a + b 在编译器看来就是 a.operator+(b) 的语法糖。运算符重载没有引入新语法,只是让已有的运算符符号对自定义类型也能工作。
可以重载和不可以重载的运算符
Section titled “可以重载和不可以重载的运算符”可以重载的运算符:
| 类别 | 运算符 |
|---|---|
| 算术 | + - * / % |
| 位运算 | & | ^ ~ << >> |
| 赋值 | = += -= *= /= %= &= |= ^= <<= >>= |
| 比较 | == != < > <= >= <=> (C++20) |
| 逻辑 | ! && || |
| 递增递减 | ++ -- |
| 其他 | () [] -> ->* , new delete |
不可以重载的运算符:
.(成员访问).*(成员指针访问)::(作用域解析)?:(三元条件)sizeoftypeid
- 不能创造新运算符 — 不能定义
operator@或operator**,只能重载已有的运算符 - 不能改变运算符的元数 — 二元运算符
+不能重载为一元,一元!不能重载为二元 - 不能改变优先级和结合性 —
*的优先级始终高于+ - 至少有一个操作数是自定义类型 — 不能重载两个
int的+运算
成员函数 vs 非成员函数
Section titled “成员函数 vs 非成员函数”运算符可以作为成员函数或非成员函数(通常是友元函数)来重载。
成员函数形式
Section titled “成员函数形式”左操作数是 this 所指向的对象:
class Vector2D {public: double x, y;
Vector2D(double x = 0, double y = 0) : x(x), y(y) {}
// 成员函数:a + b → a.operator+(b) Vector2D operator+(const Vector2D& rhs) const { return Vector2D(x + rhs.x, y + rhs.y); }};非成员函数形式
Section titled “非成员函数形式”两个操作数都是参数:
class Vector2D {public: double x, y; Vector2D(double x = 0, double y = 0) : x(x), y(y) {}
friend Vector2D operator+(const Vector2D& lhs, const Vector2D& rhs);};
// 非成员函数:a + b → operator+(a, b)Vector2D operator+(const Vector2D& lhs, const Vector2D& rhs) { return Vector2D(lhs.x + rhs.x, lhs.y + rhs.y);}| 运算符 | 推荐形式 | 原因 |
|---|---|---|
= += -= 等赋值类 | 成员函数 | 必须是成员函数(语言要求) |
() [] -> ->* | 成员函数 | 必须是成员函数(语言要求) |
<< >> 流运算符 | 非成员函数 | 左操作数是 std::ostream/std::istream,不是自定义类型 |
+ - * / 等算术 | 非成员函数 | 允许左右操作数的隐式转换对称 |
== != < 等比较 | 非成员函数(或 C++20 成员) | 同上 |
一元 ++ -- - ! | 成员函数 | 只操作自身,成员更自然 |
{% hint style=“info” %}
经验法则:如果运算符需要修改左操作数的状态(如 =、+=),用成员函数;如果运算符对两个操作数一视同仁(如 +、==),用非成员函数,以便两侧操作数都能进行隐式类型转换。
{% endhint %}
运算符重载的设计原则
Section titled “运算符重载的设计原则”- 语义一致 — 重载的运算符行为应符合直觉。
+应该表示「相加」或「合并」,不应该用来做减法 - 成套重载 — 如果重载了
+,通常也应该重载+=;重载了==,通常也该重载!= - 保持对称性 —
a == b为 true 时b == a也应为 true - 返回值类型合理 — 算术运算符返回新对象,赋值运算符返回引用
- 不要滥用 — 如果运算符的含义不直观,宁可用命名函数