当前位置: 首页 > 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.lryc.cn/news/605353.html

相关文章:

  • MATLAB R2023b下载与保姆级安装教程!!
  • 如何读懂 火山方舟 API 部分的内容
  • 《JWT + OAuth2统一认证授权:企业级单点登录方案》
  • SpringBoot之多环境配置全解析
  • Tlias 案例-整体布局(前端)
  • 《大唐孤勇者:韩愈传》读书笔记与经典摘要(二)
  • 【0基础PS】PS工具详解--画笔工具
  • Python 的 match-case
  • 【2025/07/30】GitHub 今日热门项目
  • 数学建模——最大最小化模型
  • “娃哈哈”387件商标还在原集团名下!
  • C++从入门到起飞之——智能指针!
  • Unity UI的未来之路:从UGUI到UI Toolkit的架构演进与特性剖析(5)
  • Tableau 2019可视化数据分析软件安装包下载安装教程
  • 微软:科技领域的创新巨头
  • 华为昇腾NPU卡 文生视频[T2V]大模型WAN2.1模型推理使用
  • 【Qt】QTime::toString(“hh:mm:ss.zzz“) 显示乱码的原因与解决方案
  • OpenWrt Network configuration
  • SpringBoot 2.7.18 升级 3.4.6
  • LLMs之Agent:GLM-4.5的简介、安装和使用方法、案例应用之详细攻略
  • Python基础--Day04--流程控制语句
  • html的onBlur
  • 洛谷刷题7.30
  • 外键列索引优化:加速JOIN查询的关键
  • 【Arch-Linux,hyprland】常用配置-已实验成功指令大全(自用)(持续更新)
  • IBM Watsonx BI:AI赋能的下一代商业智能平台
  • 2.3.1-2.3.5获取资源-建设团队- 管理团队-实施采购-指导
  • Effective C++ 条款11:在operator=中处理“自我赋值”
  • ros2 launch文件编写详解
  • Python 程序设计讲义(46):组合数据类型——集合类型:集合间运算