跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
C++

C++ 运算符重载:自定义类型的运算扩展

C++ 运算符重载允许为自定义类型定义运算符行为,使其像内置类型一样参与运算。成员函数与全局函数两种实现方式,涵盖二元、一元及关系运算符的重载语法。重点讲解前置后置 ++ 区别、赋值运算符深拷贝处理以及输入输出流重载技巧。通过复数运算实战案例,展示如何统一操作风格并简化代码,同时明确禁止重载的运算符列表及开发规范,帮助开发者掌握面向对象编程中的关键技能。

板砖工程师发布于 2026/3/29更新于 2026/6/816 浏览
C++ 运算符重载:自定义类型的运算扩展

C++ 运算符重载:自定义类型的运算扩展

在这里插入图片描述

学习目标:掌握运算符重载的核心语法与规则,能够为自定义类型重载常用运算符,实现类对象的灵活运算。 学习重点:运算符重载的基本形式、成员函数与全局函数重载的区别、常见运算符的重载实现、禁止重载的运算符。

一、运算符重载的概念与核心价值

核心结论:运算符重载是 C++ 静态多态的重要体现,允许为自定义类型(如类、结构体)重新定义运算符的行为,让自定义对象可以像内置类型一样使用运算符。

运算符重载的核心价值主要体现在三个方面:

  1. 简化代码书写:用直观的运算符替代繁琐的成员函数调用,提升代码可读性。
  2. 统一操作风格:让自定义类型的运算逻辑与内置类型保持一致,降低学习和使用成本。
  3. 扩展类型功能:根据业务需求定制运算符的行为,满足自定义类型的运算需求。

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

二、运算符重载的基本语法

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

2.1 成员函数重载语法

将运算符重载函数定义为类的成员函数,语法格式如下:

class 类名 {
public:
    返回值类型 operator 运算符 (参数列表) {
        // 自定义运算逻辑
    }
};
  • 一元运算符(如 ++、--)作为成员函数时,没有参数。
  • 二元运算符(如 +、-)作为成员函数时,只有一个参数,表示运算符右侧的操作数。
2.2 全局函数重载语法

将运算符重载函数定义为全局函数,语法格式如下:

返回值类型 operator 运算符 (参数列表) {
    // 自定义运算逻辑
}
  • 一元运算符作为全局函数时,有一个参数。
  • 二元运算符作为全局函数时,有两个参数,分别表示左右两侧的操作数。
  • 如果需要访问类的私有成员,可将全局重载函数声明为类的友元函数。

三、常见运算符的重载实现

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

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

3.1.1 成员函数重载 运算符
+
#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;
}
3.1.2 全局函数重载 + 运算符
#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;
}
3.2 一元运算符重载:以 ++ 为例

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

3.2.1 前置 ++ 运算符重载

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

#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;
}
3.2.2 后置 ++ 运算符重载

后置 ++ 表示先使用,再自增,重载时需要添加一个占位参数 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;
}
3.3 关系运算符重载:以 == 为例

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

#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;
}
3.4 输入输出运算符重载:<< 和 >>

<< 和 >> 运算符通常需要全局函数 + 友元的方式重载,因为左侧操作数是 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;
}

四、运算符重载的限制条件

4.1 禁止重载的运算符

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

  • 成员访问运算符:.
  • 成员指针访问运算符:.*
  • 作用域解析运算符:::
  • 条件运算符:?:
  • 预处理运算符:#
4.2 必须以成员函数重载的运算符

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

  • 赋值运算符:=
  • 函数调用运算符:()
  • 下标运算符:[]
  • 箭头运算符:->
4.3 重载赋值运算符 = 的注意事项

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

#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 类表示复数,重载 +、-、*、<< 运算符,实现复数的加减乘运算和输出功能。

5.1 需求分析
  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. 重载 <<:输出复数的标准格式。
5.2 完整代码实现
#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;
}
5.3 运行结果
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. 2.1 成员函数重载语法
  5. 2.2 全局函数重载语法
  6. 三、常见运算符的重载实现
  7. 3.1 二元运算符重载:以 + 为例
  8. 3.1.1 成员函数重载 + 运算符
  9. 3.1.2 全局函数重载 + 运算符
  10. 3.2 一元运算符重载:以 ++ 为例
  11. 3.2.1 前置 ++ 运算符重载
  12. 3.2.2 后置 ++ 运算符重载
  13. 3.3 关系运算符重载:以 == 为例
  14. 3.4 输入输出运算符重载:<< 和 >>
  15. 四、运算符重载的限制条件
  16. 4.1 禁止重载的运算符
  17. 4.2 必须以成员函数重载的运算符
  18. 4.3 重载赋值运算符 = 的注意事项
  19. 五、实战案例:重载运算符实现复数运算
  20. 5.1 需求分析
  21. 5.2 完整代码实现
  22. 5.3 运行结果
  23. 六、运算符重载的开发规范
  24. 七、本章总结
  • 💰 8折买阿里云服务器限时8折了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 🤖 一键搭建Deepseek满血版了解详情
  • 一键打造专属AI 智能体了解详情
极客日志微信公众号二维码

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

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

更多推荐文章

查看全部
  • C++ STL 容器实战:set 与 multiset 深度解析
  • 工业级存储芯片 CSNP32GCR01-AOW 在无人机飞控系统中的应用实践
  • 使用 Cursor 连接 GitHub 仓库并解决合并冲突
  • C++ 计算未排序数组排序后相邻元素的最大差值
  • LightRAG:轻量级检索增强生成模型与知识库构建指南
  • 大语言模型微调优化技术研究
  • GPT-4 提示工程大赛冠军技巧解析
  • 鸿蒙开发:方舟框架自适应布局能力详解
  • Metric3D v2: 零样本单目度量深度与表面法线估计基础模型
  • Silly Tavern 角色卡与世界书导入教程
  • 客户端负载均衡器详解:Spring Cloud LoadBalancer 与 Ribbon 对比
  • Python 核心知识点速查:31 个基础要点
  • 自然语言处理在客户服务领域的实战应用
  • 工控上位机开发为何首选 C#?核心优势与实战模板
  • Python 开发常用库整理汇总
  • OpenClaw Webhook 配置与使用指南
  • 基于 Spring Boot 与 Vue 的在线考试系统设计与实现
  • Mac 系统安装 OpenClaw 命令行工具指南
  • 2026 年 AI 论文写作工具盘点与实操流程
  • Nginx 安全漏洞 CVE-2025-23419 修复指南

相关免费在线工具

  • 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

  • JSON 压缩

    通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online

  • JSON美化和格式化

    将JSON字符串修饰为友好的可读格式。 在线工具,JSON美化和格式化在线工具,online