C++ 重载运算符和重载函数
在 C++ 中,“重载”是一种让同一名称对应多种行为的机制,主要分为函数重载(Function Overloading)和运算符重载(Operator Overloading)。它们都基于编译时的“重载决议”(overload resolution),根据参数类型、数目等来选择最合适的版本。
一、函数重载
- 概念
在同一作用域中,可以声明多个同名函数,它们的参数列表(参数个数或类型顺序)必须不同,返回类型不计入重载决议。 - 语法示例
// 同名不同签名 void print(int x); void print(double x); void print(const std::string& s, bool newline = true);
- 调用决议
- 编译器根据实参类型选最佳匹配(exact, promotion, standard conversion, user-defined conversion)。
- 若匹配歧义或无合适重载,则编译错误。
- 注意事项
- 默认参数可能导致冲突:
void f(int x, int y = 0); void f(int x); // 与上面冲突
- 模板函数也可与普通函数一起参与匹配,模板是一等公民:
template<typename T> void swap(T& a, T& b); void swap(int* a, int* b); // 专门化版本
- 默认参数可能导致冲突:
二、运算符重载
- 目的
让自定义类型(类/结构体)能使用内建运算符(+ - * / [] ()
等)表达直观的操作。 - 成员函数 vs. 非成员函数
- 成员形式:
返回值 operatorX(参数…)
struct Vec { double x, y; Vec operator+(const Vec& rhs) const { return { x + rhs.x, y + rhs.y }; } };
- 一元运算符(如
-v
)参数列表为空;二元如v1 + v2
,右侧为参数rhs
,左侧是*this
。
- 一元运算符(如
- 非成员形式(通常配合友元):
struct Vec { double x, y; friend Vec operator*(double s, const Vec& v) { return { v.x * s, v.y * s }; } };
- 成员形式:
- 常见运算符
- 算术运算:
+ - * / %
- 赋值与复合赋值:
=
,+=, -=, *= …
- 比较运算:
==, !=, <, >, <=, >=
- 下标、函数调用、流插入:
operator[]
,operator()
,operator<</>>
- 递增/递减、一元正负:
++, --, +-, !
- 算术运算:
- 返回值建议
- 对于
a + b
返回新对象;对a += b
返回*this &
,以支持链式调用:Vec& operator+=(const Vec& rhs) { x += rhs.x; y += rhs.y; return *this; }
- 对于
- 注意事项
- 不可新定义运算符优先级和结合性。
- 切勿滥用:有时明确命名(如
add()
,equals()
)更易读。 - 保持行为一致性:
a == b
应与!(a < b) && !(b < a)
等价。
三、最佳实践
- 清晰、直观:只有当运算符意义明确、与内建类型行为一致时才重载。
- 尽量配对:若重载
<
,最好也重载>
,<=
,>=
或使用<=>
(C++20 三路比较)。 - 异常安全:在运算中抛出异常时保证对象状态不崩溃。
- 文档说明:为重载行为写注释,避免使用者误解。
通过函数重载和运算符重载,C++ 为库设计者提供了强大而灵活的接口定制能力,但同时也需谨慎使用,以保持代码的可读性和一致性。