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

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

C++ 运算符重载通过函数重载机制允许自定义类型使用内置运算符。内容涵盖成员函数与全局函数两种语法形式,详细解析二元、一元(含前后置)及关系运算符的实现逻辑。重点阐述输入输出流重载需使用友元函数,以及赋值运算符深拷贝的必要性。文章提供复数运算实战案例,并列出禁止重载的运算符列表及开发规范,旨在帮助开发者提升代码可读性与类型扩展能力。

w795471发布于 2026/3/15更新于 2026/7/828 浏览
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. 一、运算符重载的概念与核心价值
  5. 二、运算符重载的基本语法
  6. 2.1 成员函数重载语法
  7. 2.2 全局函数重载语法
  8. 三、常见运算符的重载实现
  9. 3.1 二元运算符重载:以 + 为例
  10. 3.1.1 成员函数重载 + 运算符
  11. 3.1.2 全局函数重载 + 运算符
  12. 3.2 一元运算符重载:以 ++ 为例
  13. 3.2.1 前置 ++ 运算符重载
  14. 3.2.2 后置 ++ 运算符重载
  15. 3.3 关系运算符重载:以 == 为例
  16. 3.4 输入输出运算符重载:<< 和 >>
  17. 四、运算符重载的限制条件
  18. 4.1 禁止重载的运算符
  19. 4.2 必须以成员函数重载的运算符
  20. 4.3 重载赋值运算符 = 的注意事项
  21. 五、实战案例:重载运算符实现复数运算
  22. 5.1 需求分析
  23. 5.2 完整代码实现
  24. 5.3 运行结果
  25. 六、运算符重载的开发规范
  26. 七、本章总结
  • 免费图片AI生成工具免费生成了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 免费图片视频在线生成30秒,将你的创意变成现实开始设计
  • X/Twitter免费视频下载器免登陆无限额度免费视频解析下载了解详情
  • 100+免费在线小游戏爽一把
极客日志微信公众号二维码

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

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

更多推荐文章

查看全部
  • WebMCP:Chrome 新 API 特性与 Agentic Web 前瞻
  • PageIndex 无分块文档索引与推理检索指南
  • AIGC 个性化与定制化内容生成:技术与应用
  • Flutter 集成 BIP340 实现鸿蒙 Schnorr 签名方案
  • Spring 事务核心:@Transactional 注解与传播机制解析
  • Python 自学完整学习路线与核心技能树指南
  • OkHttp3 网络请求中的代理选择与路由策略详解
  • NumCpp 实战指南:C++ 数值计算库使用教程
  • 计算机视觉基础与实战开发指南
  • 2024 年程序员入门大模型必读书单推荐
  • 关闭 VSCode 中的 GitHub Copilot 功能
  • 网络安全工程师的困境:如何降低流量安全产品的误报率
  • Linux 进程池实现:从原理到代码实战
  • Visual C++ 运行库安装与 DLL 缺失问题解决指南
  • 三种方法让老板秒懂大模型
  • 大厂 Git 提交规范实践指南
  • Flood Fill 算法实战:图像渲染与岛屿问题解析
  • 用初中数学理解 LLM 工作原理
  • C++ 多态底层实现原理详解:虚函数表与对象模型
  • 大学生论文写作:AI 工具全流程实战指南

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如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