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

Effective C++ 条款12:复制对象时勿忘其每一个成分

Effective C++ 条款12:复制对象时勿忘其每一个成分


核心思想自定义拷贝构造函数和拷贝赋值操作符时,必须确保复制对象的所有成员变量,包括基类成分。编译器不会检查用户自定义拷贝函数中的遗漏,任何缺失都可能导致难以察觉的运行时错误。

⚠️ 1. 编译器默认拷贝函数的局限性
  • 编译器生成的拷贝函数会自动复制所有成员变量(内置类型逐位拷贝,自定义类型调用其拷贝函数)。
  • 但若用户自定义拷贝函数,编译器不再生成默认版本,且不会提醒复制所有成员。

示例:成员变量遗漏

class Customer {std::string name;int loyaltyPoints;
public:Customer(const Customer& rhs) : name(rhs.name) {}  // 遗漏loyaltyPoints!Customer& operator=(const Customer& rhs) {name = rhs.name;                               // 遗漏loyaltyPoints!return *this;}
};

后果

  • 拷贝后新对象的 loyaltyPoints随机(栈内存残留值)→ 严重BUG!

🚨 2. 继承中的基类成分遗漏

派生类自定义拷贝函数时,必须显式调用基类拷贝函数

class PriorityCustomer : public Customer {int priorityLevel;
public:PriorityCustomer(const PriorityCustomer& rhs): Customer(rhs),                  // 关键:调用基类拷贝构造priorityLevel(rhs.priorityLevel) {}PriorityCustomer& operator=(const PriorityCustomer& rhs) {Customer::operator=(rhs);         // 关键:调用基类拷贝赋值priorityLevel = rhs.priorityLevel;return *this;}
};

错误做法

// 派生类拷贝构造函数
PriorityCustomer(const PriorityCustomer& rhs): priorityLevel(rhs.priorityLevel) {} 

后果
基类成员变量(如 name, loyaltyPoints)未被复制 → 基类部分数据丢失


⚖️ 3. 拷贝构造与拷贝赋值的交互原则
操作交互原则
拷贝构造函数可调用拷贝赋值操作符? ❌ 语法错误(对象尚未构造完成)
拷贝赋值操作符可调用拷贝构造函数? ❌ 会创建新对象而非修改当前对象

黄金法则

  • 消除代码重复:将共享逻辑提取为私有工具函数(如 init()copyFrom())。
  • 禁止互相调用:拷贝构造和拷贝赋值是不同操作,需独立实现核心逻辑。

正确实践

class Customer {
private:void copyFrom(const Customer& rhs) {  // 共享的复制逻辑name = rhs.name;loyaltyPoints = rhs.loyaltyPoints;}
public:Customer(const Customer& rhs) { copyFrom(rhs); }Customer& operator=(const Customer& rhs) {copyFrom(rhs);return *this;}
};

💡 关键原则总结

  1. 复制所有成员变量
    • 检查拷贝构造函数的初始化列表和拷贝赋值操作符的函数体,确保每个成员变量都被复制。
  2. 显式处理基类成分
    • 派生类拷贝构造函数:初始化列表中调用基类拷贝构造: Base(rhs))。
    • 派生类拷贝赋值操作符:函数体内调用基类拷贝赋值Base::operator=(rhs);)。
  3. 避免拷贝函数互相调用
    • 用第三方函数(如 copyFrom())封装共享逻辑,保持代码DRY(Don’t Repeat Yourself)。

错误示例诊断:基类成分缺失的后果

PriorityCustomer a;  
PriorityCustomer b(a);  
// b的Customer部分(如name)未被复制 → 与a的基类数据不一致!
http://www.dtcms.com/a/306707.html

相关文章:

  • AD里面出现元器件PCB封装不能编辑的情况
  • UE5保姆级新手教程第六章(角色互动)
  • 低成本高可控,TEMU自养号测评的6大核心优势解析
  • 【数据可视化-76】从释永信被查,探索少林寺客流量深度分析:Python + Pyecharts 炫酷大屏可视化(含完整数据和代码)
  • 142页|中型国有企业数字化转型方法论:京东数智化转型解决方案-五化方法论
  • Apache Ignite 集群标识(Cluster ID)和集群标签(Cluster Tag)
  • Python多线程利器:重入锁(RLock)详解——原理、实战与避坑指南
  • 国产音频DA转换芯片DP7361支持192K六通道24位DA转换器
  • AI服务器中,EEPROM有哪些部件使用,需要存储哪些信息?
  • sqli-labs:Less-2关卡详细解析
  • 跨云部署实战:前端、后端 + RSYNC、全栈场景统一落地方案
  • 在macOS上使用VS Code和Clang配置C++开发环境
  • 《解密React key:虚拟DOM Diff中的节点身份锚点》
  • Undo、Redo、Binlog的相爱相杀
  • GIS工程师面试题
  • Java项目:基于SSM框架实现的济南旅游网站管理系统【ssm+B/S架构+源码+数据库+毕业论文+远程部署】
  • 力扣 hot100 Day60
  • Rabbit MQ的消息模式-Java原生代码
  • 发那科机器人P点位置号码自动变更功能为禁用状态
  • 认识ansible(入门)
  • 《嵌入式C语言笔记(十六):字符串搜索、动态内存与函数指针精要》
  • RocketMQ 核心特性解析及与 Kafka区别
  • 思途JSP学习 0730
  • DP-v2.1-mem-clean学习(3.6.7)
  • 片上变化(OCV)
  • 7.Origin2021如何绘制拟合数据图?
  • Python 之抽象方法 @abstractmethod 的理解
  • Day06_C++编程
  • 9《MySQL 教程》MySQL 的数据类型(2)
  • 云原生环境里的显示变革:Docker虚拟浏览器与cpolar穿透技术实战