让你自己定义的类型也能用 +、<、== 这些符号——写出来的代码就像操作内置类型一样自然。竞赛中最常用的是 operator<,配合 sort 实现自定义排序。
C++ 内置的 int、double 等类型可以直接用 +、-、< 这些符号运算。但如果你自己定义了一个 Point 类(表示二维坐标点),想把两个点相加,默认是不能直接写 p1 + p2 的——编译器不知道两个 Point 加在一起意味着什么。
运算符重载就是告诉编译器:当这个符号作用于我的类型时,应该做什么。
p1 + p2p1 + p2重载运算符用关键字 operator 后跟要重载的符号来定义,写在类内部:
| 1 | class MyClass { |
| 2 | public: |
| 3 | // 返回值类型 operator符号 (参数) { ... } |
| 4 | MyClass operator+(const MyClass& other) { |
| 5 | // 定义 + 的行为,返回运算结果 |
| 6 | } |
| 7 | bool operator<(const MyClass& other) { |
| 8 | // 定义 < 的行为,返回 true/false |
| 9 | } |
| 10 | }; |
const &?const MyClass& other 而不是 MyClass other,是因为引用传参不需要复制对象(更快),const 则保证函数不会修改传入的参数。这是重载运算符的惯用写法。
以二维坐标点 Point 为例,重载 + 让两个点可以直接相加:
| 1 | struct Point { |
| 2 | int x, y; |
| 3 | Point(int x, int y) : x(x), y(y) {} |
| 4 | |
| 5 | Point operator+(const Point& other) const { |
| 6 | return Point(x + other.x, y + other.y); |
| 7 | } |
| 8 | |
| 9 | bool operator==(const Point& other) const { |
| 10 | return x == other.x && y == other.y; |
| 11 | } |
| 12 | }; |
| 13 | |
| 14 | int main() { |
| 15 | Point p1(1, 2), p2(3, 4); |
| 16 | Point p3 = p1 + p2; // → Point(4, 6),调用 operator+ |
| 17 | cout << (p1 == p2); // → 0 (false),调用 operator== |
| 18 | } |
const:这表示这个函数不会修改对象自身的数据。对于只做计算、不改状态的运算符(+、==、< 等),都应该加上这个 const。这是竞赛中运算符重载最常见、最实用的场景。std::sort 在排序时会调用 < 来比较元素大小。只要你的类重载了 operator<,就能直接用 sort 排序,无需额外写比较函数。
| 1 | struct Student { |
| 2 | string name; |
| 3 | int score; |
| 4 | int age; |
| 5 | |
| 6 | bool operator<(const Student& other) const { |
| 7 | if (score != other.score) // 第一关键字:分数降序 |
| 8 | return score > other.score; |
| 9 | if (age != other.age) // 第二关键字:年龄升序 |
| 10 | return age < other.age; |
| 11 | return name < other.name; // 第三关键字:姓名字典序 |
| 12 | } |
| 13 | }; |
| 14 | |
| 15 | int main() { |
| 16 | vector<Student> v = { |
| 17 | {"Alice", 95, 16}, |
| 18 | {"Bob", 82, 17}, |
| 19 | {"Carol", 95, 15}, |
| 20 | }; |
| 21 | sort(v.begin(), v.end()); // 自动调用 operator< |
| 22 | // 排序结果:Carol(95,15) → Alice(95,16) → Bob(82,17) |
| 23 | } |
sort 内部每次比较两个元素时,都会调用你写的 operator<。sort 就能帮你排好整个序列。
priority_queue 默认也用 operator< 来比较优先级。重载了 operator<,自定义类型就能直接放进优先队列,无需额外写仿函数。
重载 << 之后,就可以直接用 cout << p 打印自定义类型,调试起来非常方便。
这个运算符比较特殊,必须写成类外的全局函数,因为左边是 ostream 而不是你的类:
| 1 | struct Point { |
| 2 | int x, y; |
| 3 | Point(int x, int y) : x(x), y(y) {} |
| 4 | }; |
| 5 | |
| 6 | // 写在类外,第一个参数是 ostream& |
| 7 | ostream& operator<<(ostream& os, const Point& p) { |
| 8 | os << "(" << p.x << ", " << p.y << ")"; |
| 9 | return os; // 返回 os 以支持链式调用 |
| 10 | } |
| 11 | |
| 12 | int main() { |
| 13 | Point p(3, 4); |
| 14 | cout << p << endl; // 输出:(3, 4) |
| 15 | cout << p << " and " << p; // 链式调用也正常 |
| 16 | } |
| 运算符 | 常见用途 | 写在类内 / 类外 |
|---|---|---|
| + - * / | 向量、矩阵、复数等数学对象的运算 | 类内(推荐) |
| < > == != | 配合 sort、priority_queue、set 的比较逻辑 | 类内(推荐) |
| << >> | cout / cin 输出输入自定义类型,调试常用 | 必须写在类外 |
| [] | 自定义容器的下标访问 | 类内 |
| ++ -- | 迭代器等场景的自增自减 | 类内 |
** 表示乘方)。* 永远比 + 先算。::、.、?:、sizeof 这几个特殊运算符。
operator符号 定义运算符的行为,让自定义类型像内置类型一样好用。operator<,写好之后 sort 和 priority_queue 都能直接用。operator<< 需要写在类外,返回 ostream& 以支持链式输出。