当前位置: 首页 > news >正文

item11--在 operator= 中处理“自我赋值

虽然像 w = w 这样的代码看起来很傻,但在使用指针或引用时,“自我赋值”经常以隐蔽的方式发生(例如 a[i] = a[j]i == j 时,或者通过不同的指针指向同一个对象)。

如果你的赋值操作符(Assignment Operator)没有正确处理这种情况,可能会导致资源泄漏数据损坏


1. 问题的根源:先删除,后复制

最典型的错误发生在管理动态资源(如指针)的类中。如果你按照“先释放旧资源,再分配新资源”的逻辑编写代码,就会在自我赋值时崩溃。

错误的示范代码:

class Bitmap { ... };
class Widget {
private:Bitmap* pb; // 指向一个堆上的对象
public:Widget& operator=(const Widget& rhs) {delete pb;                // 1. 错误!如果 rhs 是自己,这里就把自己的资源删了pb = new Bitmap(*rhs.pb); // 2. 崩溃!此时 rhs.pb 指向的内存已经被删除了return *this;}
};

发生了什么? 如果是自我赋值(this&rhs 是同一个地址),第一步 delete pb 不仅删除了当前对象的资源,也删除了 rhs 的资源(因为它们是同一个)。当第二步试图拷贝 rhs.pb 时,实际上是在访问悬空指针(Dangling Pointer)。


2. 解决方案 A:证同测试 (Identity Test)

最直观的方法是在函数开头检查地址是否相同。

Widget& operator=(const Widget& rhs) {if (this == &rhs) return *this; // 证同测试:如果是自我赋值,直接返回delete pb;pb = new Bitmap(*rhs.pb);return *this;
}
  • 优点: 阻止了自我赋值时的错误,效率高(如果是自我赋值直接跳过)。
  • 缺点: 不具备“异常安全性”(Exception Safety)。如果 new Bitmap 抛出异常(例如内存不足),pb 已经被 delete 了,导致 Widget 对象持有一个被删除的指针。

3. 解决方案 B:精心安排语句顺序 (Ordering Statements)

这是一个更稳健的方法。要在确保新资源已经成功复制之后,才删除旧资源。这天然地处理了自我赋值(因为你先复制了一份自己的副本,再删除旧的,完全没问题)。

Widget& operator=(const Widget& rhs) {Bitmap* pOrig = pb;       // 1. 记住原来的指针pb = new Bitmap(*rhs.pb); // 2. 尝试复制新资源(如果这里抛出异常,pb 保持原样,安全!)delete pOrig;             // 3. 复制成功后,删除旧资源return *this;
}
  • 优点: 既处理了自我赋值,又具备异常安全性(Exception Safe)。

4. 解决方案 C:Copy and Swap 技术 (最佳实践)

这是现代 C++ 中非常流行的惯用手法(Idiom),它利用了拷贝构造函数交换(Swap)操作。

写法 1(手动拷贝):

Widget& operator=(const Widget& rhs) {Widget temp(rhs); // 为 rhs 制作一个副本swap(temp);       // 将 *this 的数据与副本交换return *this;
} // temp 离开作用域,自动析构,带走了 *this 原来的数据

写法 2(传值拷贝 - 更简洁):

Widget& operator=(Widget rhs) { // 注意这里是传值(By Value),自动调用拷贝构造函数swap(rhs); // 将 *this 与 局部副本 rhs 交换return *this;
} // rhs 离开作用域,自动析构,带走了 *this 原来的数据
  • 优点:
    1. 代码极其简洁。
    2. 将内存管理的细节转移到了拷贝构造函数和析构函数中,逻辑复用。
    3. 强异常安全保证:如果拷贝失败,异常会在进入函数前(参数构造时)抛出,对象状态不会改变。

总结 (Takeaway)

在编写 operator= 时,必须确保当对象赋值给自己时代码也是正确的:

  1. 确保异常安全(Exception Safety)通常也能自动解决自我赋值安全问题。
  2. Copy-and-Swap 技术是处理这一问题的优雅且常用的解法。
  3. 如果手动管理资源,请记住:先复制新资源,确定成功后,再释放旧资源
http://www.jsqmd.com/news/115957/

相关文章:

  • 预见2026:家居新品首秀平台选择战略——五大核心展会深度评估与推荐 - 匠子网络
  • Item20--宁以 pass-by-reference-to-const 替换 pass-by-value
  • 研究生必备!8个免费AI论文工具,半天生成5000字问卷论文还有高信度数据
  • Item20--宁以 pass-by-reference-to-const 替换 pass-by-value
  • Item17--以独立语句将 `new` 到的对象置入智能指针
  • Item17--以独立语句将 `new` 到的对象置入智能指针
  • 3433. 统计用户被提及情况
  • Item19--设计 class 犹如设计 type
  • 国外软件,安装即时专业版!
  • Item19--设计 class 犹如设计 type
  • basic_regex
  • c++狼人杀
  • 宠物识别丨基于弱监督学习的宠物视频内容自动标注技术实践 - 指南
  • 朴易天下:道家修行的专业术语分享
  • 个人投资者的落地路径:从“说人话,做量化”到实盘前的三道关
  • 神经网络中的 block 和 module
  • item13--使用对象管理资源
  • 深入解析:蓝桥杯基础算法精讲:模拟与高精度运算实战指南
  • item12-- 拷贝一个对象的所有组成部分
  • sub_match
  • sub_match
  • 抽奖机随机号码生成:3 种算法实现 + 测试全解析(附完整代码)
  • 【零基础精通】Python 字符串全解析:从字符序列到不可变对象的深度构建
  • item14--谨慎考虑资源管理类的拷贝行为
  • python django flask酒店客房管理系统数据可视化分析系统_gq8885n3--论文md5
  • python django flask鹿幸公司员工食堂在线点餐餐饮餐桌预约管理系统的设计与实现_utcnqqs0--论文
  • error_code
  • 虚拟化初步了解
  • Miloco 深度打通 Home Assistant,实现设备级精准控制
  • 好用的大型牛场水滴粉碎机技术强的