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

进阶篇:从手写深拷贝到 std::string 与移动语义(Rule of Five)

在上一篇《C++ 浅拷贝 vs 深拷贝:从 0 开始一步一步讲透(Student 示例·含判断方法)》里,我们已经明确了结论:

  • 默认拷贝(编译器生成)对资源类通常是浅拷贝
  • 资源类想安全,就要自己实现深拷贝(或禁止拷贝 / 使用标准库替代)

这篇进阶主要解决三个“工程里一定会遇到”的点:

  1. 为什么拷贝赋值要先 delete 再 new?(自赋值怎么处理?)
  2. 为什么std::string不会出浅拷贝问题?
  3. 移动语义到底解决什么?Rule of Five 是什么?

1)拷贝赋值为什么要先deletenew

先回顾拷贝赋值发生的场景:

Student s1("Tom", 18); Student s2("Jack", 20); s2 = s1; // s2 已经存在

1.1 如果你不先释放旧资源,会发生什么?

s2原本有一块堆内存保存 "Jack"。
如果你直接new一块新内存覆盖name

name = new char[...]; // 覆盖了旧指针

那么旧的那块内存就“丢失了地址”,再也释放不了 →内存泄漏

所以拷贝赋值正确顺序必须是:

先释放旧资源,再申请新资源,再复制内容。

1.2 为什么要做“自赋值保护”?

自赋值:

s1 = s1;

如果你不判断,代码会这样走:

  • delete[] name;把自己资源删了
  • 然后还想从other.name拷贝(其实就是自己已经被删掉的那块内存)
  • 结果就是未定义行为(可能崩,可能乱码)

因此标准写法必须带上:

if (this == &other) return *this;

2)深拷贝赋值的标准写法(可直接套用)

Student& operator=(const Student& other) { if (this == &other) return *this; delete[] name; // 1) 释放旧资源 age = other.age; name = new char[strlen(other.name) + 1]; // 2) 申请新资源 strcpy(name, other.name); // 3) 拷贝内容 return *this; }

记住:拷贝构造不用先 delete(因为对象刚出生没旧资源),
拷贝赋值必须先 delete(因为对象早就有资源了)。

3)为什么std::string不会出浅拷贝问题?

上一节我们用char*是为了把“资源管理”的坑暴露出来。
工程中你更应该写成这样:

#include <string> class Student { public: std::string name; int age; };

然后:

Student s1; s1.name = "Tom"; Student s2 = s1; // 拷贝构造 Student s3; s3 = s1; // 拷贝赋值

不会崩溃,原因很简单:

std::string自己就是一个资源管理类(RAII),它内部已经把:析构 / 拷贝 / 移动 都实现好了。

所以你拷贝Student时,std::string会自动做正确的事情:

  • 该深拷贝时深拷贝
  • 该移动时移动
  • 自动释放资源,不会 double free

结论:能用标准库容器/字符串,就别手写new/delete管字符串。

4)移动语义:它不是“更安全”,而是“更快”

深拷贝是安全的,但可能很贵:大字符串/大数组拷贝成本高。

移动语义解决的是:

避免复制大块数据,改为“转移资源所有权”。

4.1 直觉理解

  • 拷贝:给你“复印一份资料”
  • 移动:把“资料原件”直接交给你,原来的那份清空

因此移动通常:

  • 不分配新内存
  • 不复制大量数据
  • 性能很好

5)Rule of Three vs Rule of Five(一定要记住)

Rule of Three(三法则)

如果你需要自定义这三个之一,通常就要考虑另外两个:

  1. 析构函数~T()
  2. 拷贝构造T(const T&)
  3. 拷贝赋值T& operator=(const T&)

因为这意味着你在“手动管理资源”。

Rule of Five(五法则)

C++11 之后加入了移动语义,所以资源类通常还要考虑:

  1. 移动构造T(T&&)
  2. 移动赋值T& operator=(T&&)

实战经验:大多数时候你用std::string / vector / unique_ptr
就不需要自己写五件套,标准库已经替你做了。

6)工程建议:三种策略怎么选?

当你写一个“资源类”时,通常三种选择:

策略 A:实现深拷贝(像上一篇 Student 那样)

  • 适用:确实需要“复制一份独立资源”
  • 成本:代码多、容易写错

策略 B:禁止拷贝(资源独占)

比如文件句柄、锁、socket 通常不希望被复制:

Student(const Student&) = delete; Student& operator=(const Student&) = delete;

策略 C:用标准库类型替代裸指针(最推荐)

  • std::string替代char*
  • std::vector替代手写动态数组
  • std::unique_ptr管理独占资源

这是最不容易出错、工程最常用的路线。

7)一句话总结(进阶版)

  • 拷贝构造/拷贝赋值只是“什么时候拷贝”
  • 浅/深拷贝才是“怎么拷贝”
  • 资源类拷贝赋值必须:自赋值保护 + 先释放旧资源再申请新资源
  • 工程里优先用std::string / vector / 智能指针,把深拷贝坑交给标准库处理
  • 移动语义解决的是“性能”,让资源转移比复制更高效

C++ 对象拷贝 / 移动 速查表

项目触发时机对象状态是否分配新内存性能成本是否需要手写典型用途
拷贝构造函数T(const T&)创建新对象时用另一个对象初始化新对象刚出生深拷贝时会中等资源类建议手写T b = a;
拷贝赋值运算符operator=已有对象被另一个对象覆盖对象已存在深拷贝时会中等资源类必须手写b = a;
移动构造函数T(T&&)创建新对象时接收临时对象新对象刚出生通常不会很低可选(性能优化)T b = std::move(a);
移动赋值运算符operator=(T&&)已有对象接收临时对象对象已存在通常不会很低可选(性能优化)b = std::move(a);

“人话理解版”

类型本质行为类比
拷贝构造复制资源复印一份资料
拷贝赋值先丢旧资料,再复印新资料覆盖旧文件
移动构造转移资源所有权把原件直接交给你
移动赋值先丢旧资料,再接收原件把旧文件扔掉,接收原件
http://www.jsqmd.com/news/366657/

相关文章:

  • ‌协议安全审计:NLP解析SSL/TLS握手漏洞的自动化扫描器‌
  • 贵州工业地坪解决方案指南 固化剂/环氧/金刚砂地坪优选 贵州惠博特专属定制 - 深度智识库
  • LoRa智能网关在智慧城市市政设施监控中的功能作用
  • 2026精选10家高清壁纸素材网站推荐,商用正版适配电商、设计全场景 - 品牌2026
  • 轻量级GAN在MEC资源争用测试中的技术突破与行业实践
  • 2026四川抗裂砂浆厂家哪家好,腻子粉、找平石膏、瓷砖胶、自流平一站式解析 - 深度智识库
  • 百万级流量下的数据库突围:分库分表从原理到实战全解析
  • Mac 和 Windows 都能用!这款神器让水印添加快到飞起
  • 贵州工业地坪施工指南:专业固化剂地坪、金刚砂地坪一站式服务商推荐 - 深度智识库
  • day82(2.10)——leetcode面试经典150
  • 实测才敢推 8个降AIGC工具:继续教育降AI率全维度测评
  • AI 对话也要看广告?OpenAI 正式测试 ChatGPT 广告系统
  • WebP 转 JPG 在线工具怎么选?几款实用网站对比推荐
  • 2026 南昌英语雅思培训教育机构推荐|雅思培训课程中心权威口碑榜单 - 老周说教育
  • 免费好用!2026AI 论文工具排行榜,新手直接抄作业
  • R-Meta分析核心技术:从热点挖掘到高级模型、助力高效科研与论文发表 - 教程
  • 成都、天津、大连、珠海:养老工作分析 by Claude Opus 4.6
  • OpenCSG(开放传神)赋能软件研发:某金融软件公司用AI Coding把研发效率提升40%
  • 2026 AI论文工具黑马榜!实测后,真正好用的都在这
  • 贵州工业地坪一站式解决方案指南,专业服务看贵州惠博特装饰材料有限公司 - 深度智识库
  • 尺寸智能管理:制造业质量变革的隐性引擎
  • 影视剪辑素材网站推荐,海量高清视频素材,剪辑师必备素材网 - 品牌2026
  • 2026 南昌英语雅思培训教育机构推荐、雅思培训课程中心权威口碑榜单 - 老周说教育
  • 【春招必看】一次性入门openlayers和cesium两个地图开发框架
  • AI训练素材及数据集供应商推荐:卓特视觉专业AI训练素材供应商 - 品牌2026
  • 2025LLM核心架构进展复盘:Attn与MOE研究精选(收藏版)
  • 高端制造首选!2026国产颗粒计数器厂家推荐清单 - 品牌推荐大师1
  • 小白程序员轻松入门:LinearRAG如何革新大模型知识检索?
  • c#ref和out
  • poll() Missing ‘window‘ in context File解决办法 blender插件开发