跳到主要内容
极客日志极客日志
首页博客AI提示词GitHub精选代理工具
搜索
|注册
博客列表
C++算法

C++ 运算符重载:让自定义类型支持运算扩展

综述由AI生成C++ 运算符重载允许为自定义类型重新定义运算符行为,使对象能像内置类型一样参与运算。了成员函数与全局函数两种重载形式,涵盖二元、一元、关系及输入输出运算符的实现细节。重点讲解了前置与后置 ++ 的区别、赋值运算符深拷贝处理以及 << 运算符的友元用法。通过 Point、Counter、Complex 等实战案例,展示了如何编写符合规范的代码,提升可读性与扩展性。

芝士奶盖发布于 2026/3/23更新于 2026/5/37 浏览
C++ 运算符重载:让自定义类型支持运算扩展

C++ 运算符重载:让自定义类型支持运算扩展

图片

在 C++ 中,运算符重载是静态多态的重要体现。它允许我们为自定义类型(如类、结构体)重新定义运算符的行为,让自定义对象可以像内置类型一样使用 +、- 等符号。这不仅能简化代码书写,还能统一操作风格,降低学习和使用成本。

需要注意的是,运算符重载不会改变运算符的优先级和结合性,也不会改变运算符的操作数个数。

运算符重载的基本语法

运算符重载的本质是函数重载,分为成员函数重载和全局函数重载两种形式。

成员函数重载

将运算符重载函数定义为类的成员函数。对于一元运算符(如 ++、--),作为成员函数时没有参数;对于二元运算符(如 +、-),只有一个参数,表示运算符右侧的操作数。

class ClassName {
public:
    // 返回值类型 operator 运算符 (参数列表) {
        // 自定义运算逻辑
    //}
};

全局函数重载

将运算符重载函数定义为全局函数。如果需要访问类的私有成员,可将全局重载函数声明为类的友元函数。

  • 一元运算符作为全局函数时,有一个参数。
  • 二元运算符作为全局函数时,有两个参数,分别表示左右两侧的操作数。
返回值类型 operator 运算符 (参数列表) {
    // 自定义运算逻辑
}

常见运算符的重载实现

二元运算符重载:以 + 为例

我们以自定义 Point 类为例,重载 + 运算符实现两个点的坐标相加。

成员函数方式

这种方式下,左操作数是调用该函数的对象本身。

#include <iostream>
using namespace std;

class Point {
public:
    int x, y;
    
    // 构造函数
    Point(int x = 0, int y = 0) : x(x), y(y) {}
    
    // 成员函数重载 + 运算符
    Point operator+(const Point& p) {
        return Point(this->x + p.x, this->y + p.y);
    }
    
    // 打印点坐标
    void print() {
        cout << "(" << x << ", " << y << ")" << endl;
    }
};

int main() {
    Point p1(1, 2), p2(3, 4);
    Point p3 = p1 + p2; // 等价于 p1.operator+(p2)
    p3.print(); // 输出 (4, 6)
    return 0;
}
全局函数方式

这种方式更灵活,特别是当左侧操作数不是当前类对象时(例如 ostream)。

#include <iostream>
using namespace std;

class Point {
public:
    int x, y;
    
    Point(int x = 0, int y = 0) : x(x), y(y) {}
    
    void print() {
        cout << "(" << x << ", " << y << ")" << endl;
    }
    
    // 声明友元函数
    friend Point operator+(const Point& p1, const Point& p2);
};

// 全局函数重载 + 运算符
Point operator+(const Point& p1, const Point& p2) {
    return Point(p1.x + p2.x, p1.y + p2.y);
}

int main() {
    Point p1(1, 2), p2(3, 4);
    Point p3 = p1 + p2; // 等价于 operator+(p1, p2)
    p3.print(); // 输出 (4, 6)
    return 0;
}

一元运算符重载:以 ++ 为例

一元运算符分为前置 ++ 和后置 ++,二者的重载方式有区别。

前置 ++ 运算符重载

前置 ++ 表示先自增,再使用,重载时没有参数。

#include <iostream>
using namespace std;

class Counter {
private:
    int count;
public:
    Counter(int c = 0) : count(c) {}
    
    // 成员函数重载前置 ++
    Counter& operator++() {
        this->count++;
        return *this; // 返回自增后的对象,支持链式操作
    }
    
    void show() {
        cout << "计数:" << count << endl;
    }
};

int main() {
    Counter c(5);
    ++c; // 等价于 c.operator++()
    c.show(); // 输出 计数:6
    Counter c2 = ++c;
    c2.show(); // 输出 计数:7
    return 0;
}
后置 ++ 运算符重载

后置 ++ 表示先使用,再自增,重载时需要添加一个占位参数 int 区分前置版本。

#include <iostream>
using namespace std;

class Counter {
private:
    int count;
public:
    Counter(int c = 0) : count(c) {}
    
    // 成员函数重载后置 ++
    Counter operator++(int) {
        Counter temp = *this; // 保存当前对象状态
        this->count++;
        return temp; // 返回自增前的对象
    }
    
    void show() {
        cout << "计数:" << count << endl;
    }
};

int main() {
    Counter c(5);
    Counter c2 = c++; // 等价于 c.operator++(0)
    c.show(); // 输出 计数:6
    c2.show(); // 输出 计数:5
    return 0;
}

关系运算符重载:以 == 为例

重载关系运算符(==、!=、<、> 等),实现自定义对象的比较逻辑。

#include <iostream>
#include <string>
using namespace std;

class Student {
private:
    string name;
    int id;
public:
    Student(string name, int id) : name(name), id(id) {}
    
    // 成员函数重载 == 运算符
    bool operator==(const Student& s) {
        return this->id == s.id; // 按学号判断是否为同一学生
    }
    
    string getName() {
        return name;
    }
};

int main() {
    Student s1("张三", 2024001);
    Student s2("李四", 2024002);
    Student s3("张三", 2024001);
    
    if (s1 == s3) {
        cout << s1.getName() << " 和 " << s3.getName() << " 是同一学生" << endl;
    } else {
        cout << "不是同一学生" << endl;
    }
    
    if (s1 == s2) {
        cout << "是同一学生" << endl;
    } else {
        cout << s1.getName() << " 和 " << s2.getName() << " 不是同一学生" << endl;
    }
    return 0;
}

输入输出运算符重载:<< 和 >>

<< 和 >> 运算符通常需要全局函数 + 友元的方式重载,因为左侧操作数是 ostream 或 istream 对象,无法直接修改其成员函数。

#include <iostream>
#include <string>
using namespace std;

class Person {
private:
    string name;
    int age;
public:
    Person(string name = "", int age = 0) : name(name), age(age) {}
    
    // 声明友元函数,重载 << 运算符
    friend ostream& operator<<(ostream& os, const Person& p);
    // 声明友元函数,重载 >> 运算符
    friend istream& operator>>(istream& is, Person& p);
};

// 重载 << 运算符,用于输出对象
ostream& operator<<(ostream& os, const Person& p) {
    os << "姓名:" << p.name << ",年龄:" << p.age;
    return os; // 返回 os,支持链式输出
}

// 重载 >> 运算符,用于输入对象
istream& operator>>(istream& is, Person& p) {
    is >> p.name >> p.age;
    return is; // 返回 is,支持链式输入
}

int main() {
    Person p;
    cout << "请输入姓名和年龄:" << endl;
    cin >> p; // 等价于 operator>>(cin, p)
    cout << "你输入的信息:" << p << endl; // 等价于 operator<<(cout, p)
    return 0;
}

运算符重载的限制条件

禁止重载的运算符

以下运算符不允许重载,使用时需注意:

  • 成员访问运算符:.
  • 成员指针访问运算符:.*
  • 作用域解析运算符:::
  • 条件运算符:?:
  • 预处理运算符:#

必须以成员函数重载的运算符

以下运算符只能通过类的成员函数重载,不能使用全局函数:

  • 赋值运算符:=
  • 函数调用运算符:()
  • 下标运算符:[]
  • 箭头运算符:->

重载赋值运算符 = 的注意事项

赋值运算符 = 是类的默认成员函数,编译器会自动生成一个浅拷贝版本。当类中包含指针成员时,必须手动重载 = 实现深拷贝,避免浅拷贝导致的内存泄漏。

#include <iostream>
#include <cstring>
using namespace std;

class String {
private:
    char* str;
public:
    // 构造函数
    String(const char* s = "") {
        str = new char[strlen(s) + 1];
        strcpy(str, s);
    }
    
    // 析构函数
    ~String() {
        delete[] str;
    }
    
    // 重载赋值运算符 =,实现深拷贝
    String& operator=(const String& s) {
        if (this == &s) { // 防止自赋值
            return *this;
        }
        // 释放当前对象的内存
        delete[] str;
        // 分配新内存并拷贝数据
        str = new char[strlen(s.str) + 1];
        strcpy(str, s.str);
        return *this; // 支持链式赋值
    }
    
    void show() {
        cout << str << endl;
    }
};

int main() {
    String s1("Hello C++");
    String s2;
    s2 = s1; // 调用重载的 = 运算符
    s2.show(); // 输出 Hello C++
    return 0;
}

核心注意点:重载赋值运算符时,必须返回 *this 的引用,否则无法支持链式赋值(如 a = b = c)。

实战案例:重载运算符实现复数运算

设计一个 Complex 类表示复数,重载 +、-、*、<< 运算符,实现复数的加减乘运算和输出功能。

需求分析

  1. 复数的形式为 a+bi,包含实部 real 和虚部 imag 两个成员变量。
  2. 重载 +:(a+bi)+(c+di)=(a+c)+(b+d)i
  3. 重载 -:(a+bi)-(c+di)=(a-c)+(b-d)i
  4. 重载 *:(a+bi)*(c+di)=(ac-bd)+(ad+bc)i
  5. 重载 <<:输出复数的标准格式。

完整代码实现

#include <iostream>
using namespace std;

class Complex {
private:
    double real; // 实部
    double imag; // 虚部
public:
    // 构造函数
    Complex(double real = 0, double imag = 0) : real(real), imag(imag) {}
    
    // 成员函数重载 + 运算符
    Complex operator+(const Complex& c) {
        return Complex(this->real + c.real, this->imag + c.imag);
    }
    
    // 成员函数重载 - 运算符
    Complex operator-(const Complex& c) {
        return Complex(this->real - c.real, this->imag - c.imag);
    }
    
    // 成员函数重载 * 运算符
    Complex operator*(const Complex& c) {
        double r = this->real * c.real - this->imag * c.imag;
        double i = this->real * c.imag + this->imag * c.real;
        return Complex(r, i);
    }
    
    // 友元函数重载 << 运算符
    friend ostream& operator<<(ostream& os, const Complex& c);
};

// 实现 << 运算符重载
ostream& operator<<(ostream& os, const Complex& c) {
    if (c.imag >= 0) {
        os << c.real << " + " << c.imag << "i";
    } else {
        os << c.real << " - " << -c.imag << "i";
    }
    return os;
}

int main() {
    Complex c1(3, 4), c2(1, -2);
    Complex c3 = c1 + c2;
    Complex c4 = c1 - c2;
    Complex c5 = c1 * c2;
    
    cout << "c1 = " << c1 << endl;
    cout << "c2 = " << c2 << endl;
    cout << "c1 + c2 = " << c3 << endl;
    cout << "c1 - c2 = " << c4 << endl;
    cout << "c1 * c2 = " << c5 << endl;
    return 0;
}

运行结果

c1 = 3 + 4i
c2 = 1 - 2i
c1 + c2 = 4 + 2i
c1 - c2 = 2 + 6i
c1 * c2 = 11 - 2i

开发规范建议

  1. 保持语义一致:重载后的运算符行为应与内置类型的语义相近,避免让使用者产生困惑。
  2. 优先使用成员函数:对于一元运算符和复合赋值运算符(如 +=、-=),优先使用成员函数重载。
  3. 输入输出用全局友元:<< 和 >> 运算符必须使用全局函数 + 友元的方式重载。
  4. 深拷贝处理:当类包含指针成员时,必须手动重载赋值运算符 = 实现深拷贝。
  5. 避免过度重载:只重载实际需要的运算符,不要为了重载而重载,以免增加代码复杂度。

总结

运算符重载的本质是函数重载,分为成员函数和全局函数两种实现方式。合理使用运算符重载可以简化代码、统一风格,是 C++ 面向对象编程的重要技能。部分运算符有固定的重载方式,如 = 必须用成员函数,<< 必须用全局友元函数。

目录

  1. C++ 运算符重载:让自定义类型支持运算扩展
  2. 运算符重载的基本语法
  3. 成员函数重载
  4. 全局函数重载
  5. 常见运算符的重载实现
  6. 二元运算符重载:以 + 为例
  7. 成员函数方式
  8. 全局函数方式
  9. 一元运算符重载:以 ++ 为例
  10. 前置 ++ 运算符重载
  11. 后置 ++ 运算符重载
  12. 关系运算符重载:以 == 为例
  13. 输入输出运算符重载:<< 和 >>
  14. 运算符重载的限制条件
  15. 禁止重载的运算符
  16. 必须以成员函数重载的运算符
  17. 重载赋值运算符 = 的注意事项
  18. 实战案例:重载运算符实现复数运算
  19. 需求分析
  20. 完整代码实现
  21. 运行结果
  22. 开发规范建议
  23. 总结
  • 💰 8折买阿里云服务器限时8折了解详情
  • GPT-5.5 超高智商模型1元抵1刀ChatGPT中转购买
  • 代充Chatgpt Plus/pro 帐号了解详情
  • 🤖 一键搭建Deepseek满血版了解详情
  • 一键打造专属AI 智能体了解详情
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • OpenClaw + GitHub Copilot GPT-5.4 技术修复指南
  • VcXsrv Windows X Server 使用指南:在 Windows 上运行 Linux 图形应用
  • Java 规则引擎核心原理与生产级落地实战
  • C++ 哈希表实现:unordered_map/set、位图与布隆过滤器
  • 发送 Webhook 到飞书机器人
  • 机器人 DH 参数模型与正运动学
  • 2026年3月13日AI热点:芯片大战、Agent爆发、安全争议
  • ToDesk 集成 ToClaw:AI Agent 实现远程桌面自动化执行
  • 探索 LLaMA3:Meta 最新语言模型技术解析与应用前景
  • CANN 生态 cann-dataset:AIGC 大模型全链路数据管理方案
  • Ascend C 实现高性能 SwiGLU 激活融合算子,加速大模型前馈网络
  • 35 道常见前端 Vue 面试题及解析
  • 修改 WSL2 网络模式为 Mirrored 以使用 Windows 代理
  • 归并排序与数组逆序对详解
  • Stable Diffusion 1.5 皮革服装 LoRA 镜像部署指南
  • Flutter 与 Web 混合开发:跨平台实现方案
  • 基于 OpenClaw 搭建三 Agent 协作写作系统
  • OpenClaw 本地部署与飞书集成指南
  • Qdrant 向量数据库与 Spring AI/LangChain4J 集成实践
  • Z-Image Turbo LoRA 微调实战指南:基于 ai-toolkit 的训练流程

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online