重载运算符

编程语言 / op-overload

本地源文件:docs/lang__op-overload.md

重载运算符

重载运算符是通过对运算符的重新定义,使得其支持特定数据类型的运算操作.重载运算符是重载函数的特殊情况.

当一个运算符出现在一个表达式中,并且运算符的至少一个操作数具有一个类或枚举的类型时,则使用重载决议(overload resolution)确定应该调用哪个满足相应声明的用户定义函数.1

通俗的讲,如果把使用「运算符」看作一个调用特殊的函数(如将 1+2 视作调用 add(1, 2)),并且这个函数的参数(操作数)至少有一个是 classstructenum 的类型,编译器就需要根据操作数的类型决定应当调用哪个自定义函数.

在 C++ 中,我们可以重载几乎所有可用的运算符.

一些可重载运算符的列举

一元运算:+(正号);-(负号);~(按位取反);++--!(逻辑非);*(取指针对应值);&(取地址);->(类成员访问运算符)等.

二元运算:+-&(按位与);[](取下标);===(赋值)等.

其它:()(函数调用);""(后缀标识符1,C++11 起);new(内存分配);,(逗号运算符);<=>(三路比较2,C++20 起)等.

限制

重载运算符存在如下限制:

  • 只能对现有的运算符进行重载,不能自行定义新的运算符.
  • 以下运算符不能被重载:::(作用域解析),.(成员访问),.*(通过成员指针的成员访问),?:(三目运算符).
  • 重载后的运算符,其运算优先级,运算操作数,结合方向不得改变.
  • &&(逻辑与)和 ||(逻辑或)的重载失去短路求值.

实现

重载运算符分为两种情况,重载为成员函数或非成员函数.

当重载为成员函数时,因为隐含一个指向当前成员的 this 指针作为参数,此时函数的参数个数与运算操作数相比少一个.

而当重载为非成员函数时,函数的参数个数与运算操作数相同.

其基本格式为(假设需要被重载的运算符为 @):

---|---

下面将给出几个重载运算符的示例.

### 基本算数运算符

下面定义了一个二维向量结构体 `Vector2D` 并实现了相应的加法和内积的重载.

重载算数运算符的例子

---|---

自增自减运算符

自增自减运算符分为两类,前置(++a)和后置(a++).为了区分前后置运算符,重载后置运算时需要添加一个类型为 int 的空置形参.

可以将前置自增理解为调用 operator++(a)a.operator++(),后置自增理解为调用 operator++(a, 0)a.operator++(0)

分别重载前后置自增运算符的例子

---|---

另外一点是,内置的自增自减运算符中,前置的运算符返回的是引用,而后置的运算符返回的是值.虽然重载后的运算符不必遵循这一限制,不过在语义上,仍然期望重载的运算符与内置的运算符在返回值的类型上保持一致.

对于类型 T,典型的重载自增运算符的定义如下:

重载定义(以 `++` 为例)| 成员函数| 非成员函数
---|---|---
前置| `T& T::operator++();`| `T& operator++(T& a);`
后置| `T T::operator++(int);`| `T operator++(T& a, int);`

### 函数调用运算符

函数调用运算符 `()` 只能重载为成员函数.通过对一个类重载 `()` 运算符,可以使该类的对象能像函数一样调用.

重载 `()` 运算符的一个常见应用是,将重载了 `()` 运算符的结构体作为自定义比较函数传入优先队列等 STL 容器中.

下面就是一个例子:给出 𝑛n![](data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7) 个学生的姓名和分数,按分数降序排序,分数相同者按姓名字典序升序排序,输出排名最靠前的人的姓名和分数.

下面定义了一个比较结构体,实现自定义优先队列的排序方式.

重载函数调用运算符的例子

---|---

比较运算符

std::sort 和一些 STL 容器中,需要用到 < 运算符.在使用自定义类型时,我们需要手动重载.

下面是一个例子,实现了和上一节相同的功能

重载比较运算符的例子

---|---

上面的代码将小于号重载为了成员函数,当然重载为非成员函数也是可以的.

重载为非成员函数

---|---

事实上,只要有了 < 运算符,则其他五个比较运算符的重载也可以很容易实现.

---|---

关于 C++20 下的三路比较运算符

如果使用 C++20 或更高版本,我们可以直接使用默认三路比较运算符简化代码.3

---|---

默认比较的顺序按照成员变量声明的顺序逐个比较.4

也可以使用自定义三路比较.此时要求选择比较内含的序关系(std::strong_orderingstd::weak_orderingstd::partial_ordering),或者返回一个对象,使得:

  • a < b,则 (a <=> b) < 0
  • a > b,则 (a <=> b) > 0
  • ab 相等或等价,则 (a <=> b) == 0

具体实现细节请参考 比较运算符 #三路比较 - cppreference

参考资料与注释:

  1. 运算符重载 - cppreference ↩↩
  1. 用户定义字面量 - cppreference
  1. 比较运算符 #三路比较 - cppreference
  1. 默认比较 - cppreference
本页面最近更新: 2026/1/7 08:56:54,更新历史 发现错误?想一起完善?在 GitHub 上编辑此页! 本页面贡献者:StudyingFather, Tiphereth-A, Enter-tainer, Ir1d, ksyx, qwqAutomaton, shuzhouliu, ZnPdCo 本页面的全部内容在CC BY-SA 4.0SATA 协议之条款下提供,附加条款亦可能应用