news 2026/4/13 4:51:18

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

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
进阶篇:从手写深拷贝到 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);

“人话理解版”

类型本质行为类比
拷贝构造复制资源复印一份资料
拷贝赋值先丢旧资料,再复印新资料覆盖旧文件
移动构造转移资源所有权把原件直接交给你
移动赋值先丢旧资料,再接收原件把旧文件扔掉,接收原件
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/12 18:50:04

【春招必看】一次性入门openlayers和cesium两个地图开发框架

春节过后&#xff0c;即将迎来26年毕业季&#xff0c;选择就业的同学&#xff0c;如果还没拿到offer&#xff0c;就要开始准备26年春招了。如果想找WebGIS相关的岗位&#xff0c;可以通过招聘信息&#xff0c;了解到企业的具体要求。其中&#xff0c;openlayers和cesium有多重要…

作者头像 李华
网站建设 2026/4/4 21:09:29

LLM 联网搜索,到底是咋回事?

0x0 序 近段时间 DeepSeek 的服务火遍了全网&#xff0c;无论是使用网页还是使用 App 都能享受到 深度思考 联网搜索 的至尊体验。奈何免费的东西往往是需要排队的&#xff0c;从年开始 DeepSeek 的服务就一度处于不可用状态&#xff0c;就算是年后&#xff0c;网络搜索也是经…

作者头像 李华
网站建设 2026/4/5 8:23:26

万字详解大模型推理加速核心原理丨茶思AI推理

本期聚焦】万字详解大模型推理加速分形原理&#xff0c;重塑资源优化体系&#xff1b;月之暗面发布Kimi K2.5&#xff0c;实现AI推理从“单体思考”到“集群作战”进化&#xff1b;Hyper3D Rodin Gen-2 Edit上线&#xff0c;3D生成推理迈入可编辑时代&#xff1b;人大联合团队发…

作者头像 李华
网站建设 2026/4/7 21:09:49

Java毕设项目:基于springboot的食品安全监测及风险预警系统的设计与实现(源码+文档,讲解、调试运行,定制等)

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

作者头像 李华
网站建设 2026/3/22 22:47:07

刷爆kafka经典面试题100道覆盖全场景!

1:Kafka 是什么?它的主要应用场景有哪些? kafka是什么? Kafka是一种分布式流事件处理平台,最初由领英开发开发,现在是 Apache 基金会的一部分。编写语言是主要是Scala和一些底层和性能模块Java编写。 它的核心功能主要包括消息队列、流处理和数据集成。…

作者头像 李华