C++ 的运算符重载(Operator Overloading)是其强大扩展性的体现,它允许开发者赋予内置运算符(如 +, -, *, << 等)处理自定义类对象的能力。


1. 基本规则与限制

在开始编写代码前,必须牢记以下“死理”:

  • 不能创建新运算符:你只能重载 C++ 语法中已有的运算符(例如不能造一个 表示幂运算)。

  • 不改变优先级和结合性* 的优先级永远高于 +

  • 至少有一个参数是类类型:不能重载处理两个 int 的运算符。

  • 不可重载的运算符

    • . (成员访问)

    • .* (成员指针访问)

    • :: (域解析)

    • ?: (条件运算符)

    • sizeof, typeid, alignof


2. 重载方式:成员函数 vs. 非成员函数

A. 成员函数重载

运算符作为类的成员。第一个操作数必须是该类的对象(由 this 指针隐式传递)。

C++

Complex operator+(const Complex& other) const {
    return Complex(this->real + other.real, this->imag + other.imag);
}

B. 非成员函数(通常是友元)重载

如果左侧操作数不是该类的对象(例如 int + Complexcout << Complex),则必须使用非成员函数。

C++

friend Complex operator+(double left, const Complex& right);

3. 分类知识点详解

3.1 算术运算符 (+, -, *, /)

通常返回一个新对象(传值返回)。建议先实现赋值运算符(如 +=),再用 += 实现 + 以减少代码冗余。

3.2 自增自减 (++, --)

分为前置和后置,通过一个占位参数 int 来区分:

  • 前置Complex& operator++()(返回引用,效率高)

  • 后置Complex operator++(int)(返回原副本,效率低)

3.3 关系运算符 (==, !=, <, >)

必须返回 bool 类型。在 C++20 中,引入了 “三路比较运算符” <=>(太空船运算符),只需定义它,编译器就能自动生成所有关系运算。

3.4 赋值运算符 (=)

  • 必须是成员函数

  • 默认赋值:编译器会生成浅拷贝赋值,如果类中有指针,必须手动重载以实现深拷贝。

  • 准则:检查自赋值(if (this == &other)),返回 *this 的引用。

3.5 流运算符 (<<, >>)

  • 必须是非成员函数。因为第一个参数是 std::ostream&std::istream&

C++

std::ostream& operator<<(std::ostream& os, const Complex& c) {
    os << c.real << " + " << c.imag << "i";
    return os; // 支持链式调用
}

3.6 函数调用运算符 (())

重载后,类的对象可以像函数一样被调用,这种对象称为仿函数(Functor)

C++

struct Linear {
    double operator()(double x) { return a * x + b; }
    double a, b;
};

4. 特殊运算符

  • 下标运算符 []:通常提供 const 和非 const 两个版本。

  • 解引用运算符 *->:常用于智能指针,必须返回指针或支持解引用的对象。

  • 类型转换运算符:例如 operator bool(),允许对象直接用于条件判断。建议加上 explicit 防止意外隐式转换。


5. 最佳实践建议 (Checklist)

场景 推荐做法
一致性 含义应与内置类型保持一致(别让 + 做减法)。
返回类型 赋值运算(=, +=)返回引用;算术运算(+, -)返回新值。
对称性 如果 a + b 能行,那么 b + a 也应该能行(通常用非成员函数实现)。
效率 参数尽可能使用 const T& 以避免不必要的拷贝。

更多推荐