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

Effective C++ 条款11:在operator=中处理“自我赋值”

Effective C++ 条款11:在operator=中处理“自我赋值”


核心思想确保拷贝赋值操作符(operator=)在对象给自己赋值时(如 a = a;)能安全运行,避免资源泄漏、指针悬挂等问题。自我赋值可能通过别名、引用或指针间接发生,需显式处理。

⚠️ 1. 自我赋值的隐藏风险

以下代码存在致命风险

class Bitmap { /*...*/ };
class Widget {Bitmap* pb; // 指向堆内存的指针
public:Widget& operator=(const Widget& rhs) {delete pb;                // 释放当前资源pb = new Bitmap(*rhs.pb); // 复制rhs的资源return *this;}
};

问题
rhsthis 是同一对象时(*this = *this;):

  1. delete pb 销毁当前对象的资源
  2. new Bitmap(*rhs.pb) 访问已被销毁的 pb未定义行为

🛡️ 2. 解决方案
方法1:证同测试(Identity Test)
Widget& operator=(const Widget& rhs) {if (this == &rhs) return *this; // 关键:检查是否自我赋值delete pb;pb = new Bitmap(*rhs.pb);return *this;
}

优点:阻止自我赋值的破坏性操作
缺点:若 new Bitmap 抛出异常,pb 指向已删除内存 → 异常不安全


方法2:精心安排语句顺序
Widget& operator=(const Widget& rhs) {Bitmap* pOrig = pb;        // 记住原指针pb = new Bitmap(*rhs.pb);  // 先复制新资源delete pOrig;              // 再释放旧资源return *this;
}

优点

  1. 自我赋值安全:复制新资源后再删除旧资源
  2. 异常安全:若 new 抛出异常,pb 保持原状

方法3:Copy and Swap(推荐)
class Widget {void swap(Widget& rhs); // 交换*this和rhs的所有成员
public:Widget& operator=(const Widget& rhs) {Widget temp(rhs);  // 创建副本(调用拷贝构造函数)swap(temp);        // 交换*this和副本return *this;      // 副本离开作用域自动销毁旧资源}
};

优化版(利用值传递)

Widget& operator=(Widget rhs) { // 值传递:自动调用拷贝构造swap(rhs);                 // 交换*this和副本return *this;              // 副本销毁旧资源
}

优势

  • 代码简洁
  • 天然处理自我赋值和异常安全

💡 关键原则总结

方案自我赋值安全异常安全代码简洁性
证同测试★★☆
语句顺序调整★★★
Copy and Swap★★★★
  1. 自我赋值的隐蔽性
    • 可能通过别名(a = b;ab 指向同一对象)或链式操作(arr[i] = arr[j];)发生。
  2. 异常安全的重要性
    • 若资源分配失败(如 new 抛出异常),对象必须保持有效状态。
  3. 终极解决方案:Copy and Swap
    • 结合拷贝构造函数和 swap,确保强异常安全和自我赋值安全。

示例代码:Copy and Swap 实现

class Widget {Bitmap* pb;void swap(Widget& rhs) noexcept {using std::swap;swap(pb, rhs.pb);}
public:Widget& operator=(Widget rhs) { // 按值传递swap(rhs);return *this;}
};
http://www.lryc.cn/news/605325.html

相关文章:

  • ros2 launch文件编写详解
  • Python 程序设计讲义(46):组合数据类型——集合类型:集合间运算
  • 【百卷编程】Go语言大厂高级面试题集
  • 如何修改VM虚拟机中的ip
  • 2024 年 NOI 最后一题题解
  • 《汇编语言:基于X86处理器》第10章 复习题和练习
  • 歌尔微报考港交所上市:业绩稳增显韧性,创新引领生态发展
  • S3、SFTP、FTP、FTPS 协议的概念、对比与应用场景
  • openwrt中br-lan,eth0,eth0.1,eth0.2
  • 第2章 cmd命令基础:常用基础命令(3)
  • cmake_parse_arguments()构建清晰灵活的 CMake 函数接口
  • G9打卡——ACGAN
  • 获取TensorRT引擎文件(.engine)版本号的几种方法
  • 2022 年 NOI 最后一题题解
  • 数据集相关类代码回顾理解 | DataLoader\datasets.xxx
  • 【高等数学】第七章 微分方程——第四节 一阶线性微分方程
  • 【支持Ubuntu22】Ambari3.0.0+Bigtop3.2.0——Step4—时间同步(Chrony)
  • Spark的宽窄依赖
  • 《设计模式之禅》笔记摘录 - 11.策略模式
  • uniapp-vue3来实现一个金额千分位展示效果
  • uniapp实现微信小程序导航功能
  • 思途JSP学习 0730
  • LeetCode 刷题【22. 括号生成】
  • Winform 渐变色 调色板
  • 代码随想录算法训练营第五十六天|动态规划part6
  • C语言基础11——结构体1
  • AutoSAR(MCAL) --- ADC
  • VoIP技术全面深度学习指南:从原理到实践的认知进化
  • 【GEO从入门到精通】生成式引擎与其他 AI 技术的关系
  • Linux ARM 平台 C 语言操作 Excel 文件的常用库与工具汇总(支持 xls 和 xlsx)