news 2026/5/16 9:14:05

【c++面向对象编程】第21篇:运算符重载基础:语法、规则与不可重载的运算符

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【c++面向对象编程】第21篇:运算符重载基础:语法、规则与不可重载的运算符

目录

一、为什么需要运算符重载?

二、基本语法

两种实现方式

示例:加法运算符

三、成员函数 vs 全局函数:如何选择?

指导原则

四、可以重载的运算符

算术运算符

关系/逻辑运算符

位运算符

自增自减

下标/调用/成员访问

赋值/内存管理

逗号/指针解引用

五、不能重载的运算符

还有几个特殊情况

危险示例:重载 && 失去短路求值

六、运算符重载的规则和限制

1. 不能发明新运算符

2. 不能改变优先级和结合性

3. 不能改变操作数个数

4. 不能改变内置类型的行为

5. 至少一个操作数是用户自定义类型

七、完整例子:有理数类的运算符重载

八、常见错误

1. 忘记 const 正确性

2. 返回类型错误

3. 没有处理自赋值(+= 等)

4. 重载 && 或 || 以为有短路求值

九、这一篇的收获


一、为什么需要运算符重载?

写一个复数类Complex,没有运算符重载时:

cpp

Complex a(1, 2), b(3, 4); Complex c = add(a, b); // 不能写 a + b Complex d = multiply(a, b); // 不能写 a * b

这很不直观。数学上复数就应该用+-*运算。运算符重载让代码更自然:

cpp

Complex a(1, 2), b(3, 4); Complex c = a + b; // 清晰! Complex d = a * b; // 直观!

本质:运算符重载就是定义特殊的函数——函数名是operator加上运算符符号。


二、基本语法

两种实现方式

方式函数名参数个数示例
成员函数operator@少一个(左侧对象是this)a + ba.operator+(b)
全局函数operator@全部作为参数a + boperator+(a, b)

示例:加法运算符

成员函数版本

cpp

class Complex { double real, imag; public: Complex(double r, double i) : real(r), imag(i) {} Complex operator+(const Complex& other) const { return Complex(real + other.real, imag + other.imag); } }; Complex a(1, 2), b(3, 4); Complex c = a + b; // 等价于 a.operator+(b)

全局函数版本(通常需要友元):

cpp

class Complex { double real, imag; public: Complex(double r, double i) : real(r), imag(i) {} friend Complex operator+(const Complex& a, const Complex& b); }; Complex operator+(const Complex& a, const Complex& b) { return Complex(a.real + b.real, a.imag + b.imag); }

三、成员函数 vs 全局函数:如何选择?

运算符推荐方式原因
=()[]->必须是成员函数C++语法规定
+-*/成员或全局均可通常用成员函数
<<>>必须是全局函数左操作数不是本类对象
双目运算符两种都行看是否需要访问私有成员
单目运算符成员函数更自然++a对应a.operator++()

指导原则

  1. 左操作数不是你控制的类型→ 必须用全局函数

    cpp

    // cout << obj,左操作数是ostream,你改不了ostream friend ostream& operator<<(ostream& os, const MyClass& obj);
  2. 需要对称性→ 用全局函数(比如int + ComplexComplex + int都支持)

  3. 自然属于对象的行为→ 用成员函数(比如obj += other


四、可以重载的运算符

C++大部分运算符都可以重载(共约40个):

算术运算符

text

+ - * / % += -= *= /= %=

关系/逻辑运算符

text

== != < > <= >= && || !

位运算符

text

& | ^ ~ << >> &= |= ^= <<= >>=

自增自减

text

++ -- (前置和后置)

下标/调用/成员访问

text

[] () -> ->

赋值/内存管理

text

= new delete new[] delete[]

逗号/指针解引用

text

, *

五、不能重载的运算符

有5个运算符不能重载(C++语法禁止):

运算符名称不能重载的原因
::作用域解析操作的是类型和命名空间,不是值
.成员访问保持内置语义,保证成员访问的安全性
.*成员指针解引用语义复杂,重载会混乱
?:三元条件第二个参数可以是表达式,重载无法模拟短路求值
sizeof获取大小编译时运算,不能变成运行时函数

还有几个特殊情况

运算符情况说明
=可以重载但必须是成员函数,且编译器默认生成
()可以重载必须是成员函数(仿函数)
[]可以重载必须是成员函数
->可以重载必须是成员函数,且返回值必须是指针或可重载->的对象
&&||可以重载但会失去短路求值,强烈不推荐

危险示例:重载&&失去短路求值

cpp

bool operator&&(const MyClass& a, const MyClass& b) { // 编译器会求值两个操作数后才调用这个函数 // 短路求值失效! }

规则:除非有特殊需求,否则不要重载&&||,


六、运算符重载的规则和限制

1. 不能发明新运算符

cpp

// ❌ 不能定义 @ 或 # 这种新符号 MyClass operator@(MyClass a, MyClass b); // 错误

2. 不能改变优先级和结合性

cpp

// + 的优先级永远高于 ==,无法改变

3. 不能改变操作数个数

cpp

// ++ 永远是单目,不能变成三目 MyClass operator++(MyClass a, MyClass b, MyClass c); // 错误

4. 不能改变内置类型的行为

cpp

// ❌ 不能重载 int 的 + int operator+(int a, int b); // 错误

5. 至少一个操作数是用户自定义类型

cpp

// ✅ 正确:有一个是 Complex Complex operator+(const Complex& a, const Complex& b); // ❌ 错误:两个都是内置类型 int operator+(int a, int b); // 错误

七、完整例子:有理数类的运算符重载

cpp

#include <iostream> #include <numeric> // for gcd using namespace std; class Rational { private: int num; // 分子 int den; // 分母 void reduce() { int g = gcd(num, den); num /= g; den /= g; if (den < 0) { num = -num; den = -den; } } public: Rational(int n = 0, int d = 1) : num(n), den(d) { if (den == 0) throw invalid_argument("分母不能为0"); reduce(); } // 算术运算符(成员函数版本) Rational operator+(const Rational& other) const { return Rational(num * other.den + other.num * den, den * other.den); } Rational operator-(const Rational& other) const { return Rational(num * other.den - other.num * den, den * other.den); } Rational operator*(const Rational& other) const { return Rational(num * other.num, den * other.den); } Rational operator/(const Rational& other) const { return Rational(num * other.den, den * other.num); } // 复合赋值运算符(成员函数) Rational& operator+=(const Rational& other) { *this = *this + other; return *this; } // 比较运算符 bool operator==(const Rational& other) const { return num == other.num && den == other.den; } bool operator<(const Rational& other) const { return num * other.den < other.num * den; } // 输入输出运算符(必须是全局函数,后面详解) friend ostream& operator<<(ostream& os, const Rational& r); friend istream& operator>>(istream& is, Rational& r); }; // 全局函数:输出 ostream& operator<<(ostream& os, const Rational& r) { if (r.den == 1) os << r.num; else os << r.num << "/" << r.den; return os; } // 全局函数:输入 istream& operator>>(istream& is, Rational& r) { is >> r.num; char slash; is >> slash; if (slash == '/') is >> r.den; else r.den = 1; r.reduce(); return is; } int main() { Rational a(1, 2); // 1/2 Rational b(2, 3); // 2/3 cout << a << " + " << b << " = " << a + b << endl; cout << a << " - " << b << " = " << a - b << endl; cout << a << " * " << b << " = " << a * b << endl; cout << a << " / " << b << " = " << a / b << endl; Rational c(3, 4); c += Rational(1, 4); cout << "c += 1/4 -> " << c << endl; cout << "1/2 == 2/4 ? " << (Rational(1,2) == Rational(2,4)) << endl; cout << "1/2 < 2/3 ? " << (Rational(1,2) < Rational(2,3)) << endl; return 0; }

输出:

text

1/2 + 2/3 = 7/6 1/2 - 2/3 = -1/6 1/2 * 2/3 = 1/3 1/2 / 2/3 = 3/4 c += 1/4 -> 1 1/2 == 2/4 ? 1 1/2 < 2/3 ? 1

八、常见错误

1. 忘记 const 正确性

cpp

Complex operator+(Complex& other) { ... } // 无法加const对象

应该用const Complex&参数,函数本身也要是const(成员函数时)。

2. 返回类型错误

cpp

Complex operator+(const Complex& a, const Complex& b) { Complex result; // ... return &result; // ❌ 不要返回局部变量的指针/引用 }

3. 没有处理自赋值(+= 等)

cpp

Complex& operator+=(const Complex& other) { real += other.real; imag += other.imag; return *this; // ✅ 返回引用,支持链式调用 }

4. 重载&&||以为有短路求值

重载后两个操作数都会被求值,行为与内置&&不同,容易迷惑使用者。


九、这一篇的收获

你现在应该理解:

  • 语法operator@定义运算符重载函数

  • 实现方式:成员函数(左操作数是 this)vs 全局函数(两侧对称)

  • 不可重载的5个::..*?:sizeof

  • 规则:不能发明新运算符、不能改优先级/结合性、至少一个操作数是自定义类型

  • 选择原则=()[]->必须是成员;<<>>必须是全局;其余看设计

💡 小作业:实现一个Vector3D类(x, y, z),重载+-*(点积,返回 double)、==<<。测试v1 + v2v1 * v2等操作。


下一篇预告:第22篇《输入输出运算符重载:<< 与 >> 的友元实现》——为什么cout << obj必须用全局函数?为什么要用友元?如何正确实现<<>>支持链式输出/输入?下篇详解。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/16 9:05:33

Wwise与Godot音频集成:专业交互式音频引擎在开源游戏开发中的应用

1. 项目概述&#xff1a;连接两大音频巨头的桥梁如果你是一名游戏开发者&#xff0c;尤其是使用Godot引擎&#xff0c;同时又对音频品质有着近乎偏执的追求&#xff0c;那么你很可能听说过&#xff0c;或者正在为“Wwise”和“Godot”这两个名字如何无缝结合而头疼。alessandro…

作者头像 李华
网站建设 2026/5/16 9:03:08

clrun:远程脚本安全隔离执行工具的设计原理与工程实践

1. 项目概述与核心价值最近在折腾一些自动化脚本和持续集成流程时&#xff0c;发现一个挺有意思的开源项目&#xff0c;叫cybertheory/clrun。乍一看这个名字&#xff0c;可能有点摸不着头脑&#xff0c;但如果你也经常在命令行里和一堆脚本、工具链打交道&#xff0c;尤其是涉…

作者头像 李华
网站建设 2026/5/16 9:03:06

LaTeX中文排版终极解决方案:一站式字体配置指南

LaTeX中文排版终极解决方案&#xff1a;一站式字体配置指南 【免费下载链接】latex-chinese-fonts Simplified Chinese fonts for the LaTeX typesetting. 项目地址: https://gitcode.com/gh_mirrors/la/latex-chinese-fonts 你是否在使用LaTeX排版中文文档时&#xff0…

作者头像 李华