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

C++ 类与对象:运算符重载、赋值与取址详解

C++ 类与对象机制涵盖运算符重载规则、赋值运算符深拷贝实现及取地址符的特殊应用。重点解析编译器默认行为与手动实现的差异,通过日期类和栈类示例演示浅拷贝风险与深拷贝方案,并探讨取地址重载在安全控制场景下的用法。

刀狂发布于 2026/3/26更新于 2026/6/412 浏览
C++ 类与对象:运算符重载、赋值与取址详解

在这里插入图片描述

一、前置知识:运算符重载

在 C++ 中,运算符本质上类似于函数。比如加法操作,相当于调用一个函数,左右操作数就是参数。一元运算符只有一个参数,二元运算符有两个。因此,我们可以像重载函数一样重载运算符,让同一个符号根据操作数的不同调用不同的实现,从而支持多态。

不过要注意,语法内置的运算符不能随意重载。例如两个整型相加的规则是确定的,无法更改。但如果操作数是自定义类型(如类),编译器不知道如何运算,这时就需要我们定义规则。比如日期类加上一个整数,表示增加天数,这就是有意义的重载场景。

运算符重载的核心规则

  1. 转换机制:当运算符用于类类型对象时,必须转换为对应的重载函数调用,否则编译报错。
  2. 命名规范:重载函数名由 operator 加运算符符号组成,如 operator+。它和普通函数一样有返回类型、参数列表和函数体。
  3. 参数数量:重载函数的参数个数等于运算符作用的操作数数量。一元运算符一个参数,二元运算符两个参数。左侧传给第一个参数,右侧传给第二个。
  4. 成员函数特性:如果作为成员函数,第一个操作数默认通过隐式的 this 指针传递。因此二元运算符作为成员函数时,只需声明一个参数(右操作数)。
  5. 优先级不变:重载后优先级和结合性与内置类型一致,且不能创建新符号(如 @)。
  6. 不可重载符号:.、.*、::、sizeof、?: 这五个运算符不能重载。
  7. 至少一个类类型:重载至少有一个参数必须是类类型,不能改变内置类型的含义(如 int + int)。
  8. 按需重载:只有重载后有意义的运算符才需要写。例如日期相减有意义,但日期相加通常无意义。
  9. 自增自减区分:前置 ++ 无参,后置 ++ 多一个 int 形参以区分重载。
  10. 流运算符:<< 和 >> 建议重载为全局函数,避免 this 抢占左操作数位置,保证 cout << obj 的写法自然。

实战示例:日期类相等运算符

我们以日期类的相等运算符为例。返回值应为 bool,参数为另一个日期对象。由于是成员函数,this 指向当前对象,只需传入比较对象。

#include <iostream>
using namespace std;

class Date {
public:
    // 构造函数
    Date(int year = 2025, int month = 1, int day = 1) {
        this->_year = year;
        this->_month = month;
        this->_day = day;
    }

    // 相等运算符重载
    bool operator==(const Date& d) const {
        return this->_year == d._year && 
               this->_month == d._month && 
               this->_day == d._day;
    }

private:
    int _year;
    int _month;
    int _day;
};

int main() {
    Date d1(2025, 1, 1);
    Date d2(2025, 1, 9);
    
    // 直接使用运算符,编译器会将其转换为函数调用
    if (d1 == d2) {
        cout << "两个日期相等" << endl;
    } else {
        cout << "两个日期不相等" << endl;
    }
    return 0;
}

运行结果会显示不相等。这里的关键在于,虽然代码写的是 d1 == d2,但在底层汇编层面,它已经被编译器自动转换成了 d1.operator==(d2)。这种透明性让我们能像使用内置类型一样使用自定义类型。

注意:不能重载内置类型运算符。尝试对 int 进行重载会导致编译错误,因为至少需要一个操作数是类类型。

二、赋值重载

赋值重载也是类的默认成员函数之一。如果不手动编写,编译器会生成一个默认的赋值函数。理解它的行为以及何时需要自己实现,是掌握类机制的关键。

默认生成的赋值重载行为

对于内置类型,默认赋值函数执行浅拷贝,即按字节复制。对于自定义类型,它会递归调用成员变量的赋值函数。这意味着如果类中包含指针成员,默认行为可能导致两个对象共享同一块堆内存,析构时引发重复释放错误。

判断是否需要手动实现赋值重载,可以参考以下技巧:

  1. 看析构/拷贝构造:如果写了析构函数或拷贝构造,通常意味着涉及资源管理,此时应配套实现赋值重载。
  2. 看堆空间:检查是否有内置类型成员变量指向堆空间(如 int*)。如果有,必须手动深拷贝。

手动实现赋值重载

以栈类(Stack)为例,它包含动态分配的数组。如果依赖默认赋值,两个栈对象将指向同一内存,修改一个会影响另一个,且析构时会崩溃。

我们需要手动实现深拷贝,并遵循以下原则:

  1. 成员函数:赋值运算符必须重载为成员函数。
  2. 参数引用:参数建议使用 const 引用,避免拷贝并防止权限放大。
  3. 返回值:返回当前对象的引用(*this),以支持连续赋值(如 a = b = c)。
  4. 自赋值检查:必须检查 this != &other,防止给自己赋值导致内存泄漏或数据覆盖。
class Stack {
public:
    Stack(int n = 10) {
        this->_arr = (int*)malloc(n * sizeof(int));
        if (this->_arr == nullptr) {
            perror("malloc");
            return;
        }
        this->_top = 0;
        this->_capacity = n;
    }

    ~Stack() {
        if (this->_arr) free(this->_arr);
        this->_top = this->_capacity = 0;
    }

    // 拷贝构造
    Stack(const Stack& st) {
        this->_arr = (int*)malloc(st._capacity * sizeof(int));
        for (int i = 0; i < st._top; i++) {
            this->_arr[i] = st._arr[i];
        }
        this->_top = st._top;
        this->_capacity = st._capacity;
    }

    // 赋值重载
    Stack& operator=(const Stack& st) {
        if (this != &st) { // 自赋值检查
            free(this->_arr); // 先释放原有资源
            this->_arr = (int*)malloc(st._capacity * sizeof(int));
            for (int i = 0; i < st._top; i++) {
                this->_arr[i] = st._arr[i];
            }
            this->_top = st._top;
            this->_capacity = st._capacity;
        }
        return *this;
    }

    void push(int x) {
        this->_arr[this->_top++] = x;
    }

private:
    int* _arr;
    int _top;
    int _capacity;
};

int main() {
    Stack st1;
    st1.push(2);
    st1.push(3);
    Stack st2;
    st2 = st1; // 调用赋值重载
    return 0;
}

区分拷贝构造与赋值重载

两者容易混淆,核心区别在于对象是否已存在:

  • 拷贝构造:对象正在被创建,用另一个对象初始化。例如 Date d2(d1); 或 Date d2 = d1;(注意:这里的 = 是初始化,不是赋值)。
  • 赋值重载:对象已存在,将值赋给现有对象。例如 d2 = d1;。

调试时可以通过断点观察调用时机来确认。

三、取地址重载

取地址运算符 (&) 分为普通版本和 const 版本。编译器默认生成的版本足够日常使用,但在特殊场景下,我们可以自定义它们来控制访问权限。

例如,如果不想让外部轻易获取对象地址,可以返回空指针或特定地址。这常用于封装安全控制。

class Stack {
public:
    // 普通取地址重载
    Stack* operator&() {
        return nullptr; // 拒绝获取地址
    }

    // const 取地址重载
    Stack* operator&() const {
        return nullptr;
    }
};

int main() {
    Stack st1;
    Stack* ps = &st1; // 调用重载函数,ps 为 nullptr
    return 0;
}

运行后会发现无法获取有效地址。除非有特殊的安全需求,否则建议直接使用编译器默认生成的版本。

目录

  1. 一、前置知识:运算符重载
  2. 运算符重载的核心规则
  3. 实战示例:日期类相等运算符
  4. 二、赋值重载
  5. 默认生成的赋值重载行为
  6. 手动实现赋值重载
  7. 区分拷贝构造与赋值重载
  8. 三、取地址重载
  • 💰 8折买阿里云服务器限时8折了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 🤖 一键搭建Deepseek满血版了解详情
  • 一键打造专属AI 智能体了解详情
极客日志微信公众号二维码

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

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

更多推荐文章

查看全部
  • STL map/multimap 深度解析:接口用法与核心特性
  • 家庭 AI 助手实战:QQ 机器人接入 OpenClaw
  • AI 生成前端 UI 的三步优化技巧
  • WebMCP 详解:让网页成为 AI 智能体的工具库
  • Python 异步编程与协程实战
  • 深度解析 AI 如何重构技术栈:从工具到思维伙伴
  • Stable Diffusion WebUI 无障碍改造:键盘导航与屏幕阅读器适配
  • 数据结构:栈与队列的应用及矩阵压缩存储
  • 互联网程序员薪资现状与职业发展思考
  • Python 网络爬虫入门实战:从零抓取小说内容
  • Collabora Online 开源协作办公套件快速部署指南
  • SolidWorks 集成 DeepSeek AI 自动生成 VBA 宏教程
  • LLaMA-Factory 全流程模型训练与推理
  • 通义万相 2.1:AIGC 创作新引擎与实战指南
  • Claude Code 安装与使用指南:配置、命令及 IDE 集成
  • Spring 配置文件详解:Properties 与 YAML 格式对比及实战
  • 利用 Figma 插件将 AI 生成的 HTML 原型导入 Axure
  • 深入解析 π₀ 与 π₀.5:Physical Intelligence 的机器人基础模型演进
  • MySQL 用户管理与权限配置
  • Ubuntu 22.04/24.04 安装 ROS2 教程(Humble / Jazzy)

相关免费在线工具

  • 加密/解密文本

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