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

C++ 运算符重载实战:让自定义类型像内置类型一样运算

C++ 运算符重载允许为类或结构体重新定义运算符行为,使自定义对象能像内置类型一样参与运算。核心在于函数重载,分为成员函数和全局函数两种形式。成员函数重载二元运算符时仅有一个参数,而全局函数需要两个参数且常需声明为友元以访问私有成员。一元运算符的前置与后置版本通过是否有占位参数 int 区分。赋值运算符必须使用成员函数实现,且需注意深拷贝以防内存泄漏。输入输出流运算符通常采用全局友元方式。合理使用运算符重载能显著提升代码可读性与一致性,但不可改变运算符优先级及结合性,部分运算符如作用域解析符禁止重载。

虚拟内存发布于 2026/3/23更新于 2026/9/1055 浏览
C++ 运算符重载实战:让自定义类型像内置类型一样运算

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

在这里插入图片描述

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

不过要注意,运算符重载不会改变运算符的优先级和结合性,也不会改变操作数的个数。理解这一点,是掌握重载的关键。

核心语法:成员函数与全局函数

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

成员函数重载

将运算符函数定义为类的成员时,一元运算符没有参数,二元运算符只有一个参数(右侧操作数)。

class ClassName {
public:
    ReturnType operatorOp(ParamList) {
        // 自定义逻辑
    }
};

全局函数重载

作为全局函数时,一元运算符有一个参数,二元运算符有两个参数。如果需要访问私有成员,需将其声明为友元。

ReturnType operatorOp(ParamList) {
    // 自定义逻辑
}

常见运算符实现细节

二元运算符:以 + 为例

下面是一个 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;
}
全局函数方式

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

#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;
}

一元运算符:前置与后置 ++

前置 ++ 先自增后使用,后置 ++ 先使用后自增。区别在于后置版本需要一个 int 占位参数来区分。

前置 ++
#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 参数只是占位符,实际调用时传入 0。

#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 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; // 返回流对象支持链式输出
}

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

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

限制条件与注意事项

并非所有运算符都能重载。以下情况需要特别注意:

  1. 禁止重载的运算符:

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

    • 赋值运算符 =
    • 函数调用运算符 ()
    • 下标运算符 []
    • 箭头运算符 ->
  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;
}

实战案例:复数运算

设计一个 Complex 类,重载加减乘及输出运算符。

#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. 开发规范建议

更多推荐文章

查看全部
  • HarmonyOS6 RcImage 组件实战指南与案例解析
  • JavaScript 中的赋值与相等操作符:=、== 和 === 详解
  • 使用 Mistral 和 Llama2 构建 AI 聊天机器人
  • IntelliJ IDEA 实用插件:GitToolBox 使用指南
  • GPEN 批量处理断点续传功能设计与实现
  • 提升 AI 模型能力的 10 个必备技能指南
  • 从城市规划到Prompt Engineering:一年转行沉浮录
  • MicroG 在 HarmonyOS 上的签名兼容方案与配置指南
  • 网络安全学习指南:核心知识与路径
  • OpenGlass:大模型赋能的开源智能眼镜方案,支持语音与 AR
  • AI 自动化测试:接口测试全流程自动化的实现方法
  • 无人机智能巡检在城市生命线管理中的应用实践
  • GitHub Copilot 使用体验与优缺点分析
  • Rust + LLM 开发:构建智能 AI 运维命令行助手
  • 基于 2-RSS-1U 的双足机器人并联踝关节分析与实现
  • LeetCode 1419 数青蛙:基于模拟的状态机解法
  • 普通产品经理转型 AI 产品经理:核心技能与能力升级指南
  • Linux TCP 可靠性与性能优化详解:从确认应答到拥塞控制
  • 基于 Playwright 封装 Web 爬虫并隐藏自动化特征
  • 全国大学生智能车竞赛智慧医疗机器人惯导与避障思路分享

相关免费在线工具

  • 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