在 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++ 为库设计者提供了强大而灵活的接口定制能力,但同时也需谨慎使用,以保持代码的可读性和一致性。