Skip to content

8.1 重载的基本规则

运算符重载本质上是定义一个名为 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

不可以重载的运算符:

  • .(成员访问)
  • .*(成员指针访问)
  • ::(作用域解析)
  • ?:(三元条件)
  • sizeof
  • typeid
  1. 不能创造新运算符 — 不能定义 operator@operator**,只能重载已有的运算符
  2. 不能改变运算符的元数 — 二元运算符 + 不能重载为一元,一元 ! 不能重载为二元
  3. 不能改变优先级和结合性* 的优先级始终高于 +
  4. 至少有一个操作数是自定义类型 — 不能重载两个 int+ 运算

运算符可以作为成员函数非成员函数(通常是友元函数)来重载。

左操作数是 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);
}
};

两个操作数都是参数:

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 %}

  1. 语义一致 — 重载的运算符行为应符合直觉。+ 应该表示「相加」或「合并」,不应该用来做减法
  2. 成套重载 — 如果重载了 +,通常也应该重载 +=;重载了 ==,通常也该重载 !=
  3. 保持对称性a == b 为 true 时 b == a 也应为 true
  4. 返回值类型合理 — 算术运算符返回新对象,赋值运算符返回引用
  5. 不要滥用 — 如果运算符的含义不直观,宁可用命名函数