news 2026/6/9 21:23:30

从拷贝到移动:C++ 移动构造与移动赋值是怎么被逼出来的?(附完整示例)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
从拷贝到移动:C++ 移动构造与移动赋值是怎么被逼出来的?(附完整示例)

很多人在学习 C++ 时,第一次看到下面这两个函数会一脸懵:

Box(Box&& other); // 移动构造 Box& operator=(Box&& other); // 移动赋值

这两个函数看起来像“高级语法”,
但实际上它们不是凭空出现的,而是被性能问题 + 临时对象浪费一步步逼出来的进化结果。

本文从“问题”出发,再用一个完整可运行的例子把它讲透。

一、最初阶段:只有拷贝构造

最早 C++ 只有拷贝语义:

Box(const Box& other);

含义就是:

复制一份资源

示例:

Box a(10); Box b = a; // 拷贝构造

如果资源很小,问题不大。
但当成员是:

  • std::string
  • std::vector
  • 大数组
  • 文件句柄
  • 网络连接

复制就会变得慢 + 占内存

二、问题升级:临时对象的浪费

看一个常见函数:

Box createBox() { Box b(10); return b; }

调用:

Box a = createBox();

如果只有拷贝构造,流程是:

构造 b 拷贝构造 a 析构 b

问题在于:

b 马上要死了,还完整复制一份资源,纯浪费。

于是一个需求出现了:

能不能不复制,而是“搬走资源”?

三、移动语义诞生 —— “搬家”而不是“复印”

移动语义的核心思想:

如果对象马上要被销毁,就不要复制资源,直接转移所有权。

这就引出了移动构造移动赋值

四、完整示例代码(核心)

下面是一个完整可运行的类,包含:

  • 构造
  • 拷贝构造
  • 移动构造
  • 拷贝赋值
  • 移动赋值
  • 析构
#include <iostream> using namespace std; class Box { public: int* data; // 构造 Box(int v) { data = new int(v); cout << "构造\n"; } // 拷贝构造 Box(const Box& other) { data = new int(*other.data); cout << "拷贝构造\n"; } // 移动构造 Box(Box&& other) { data = other.data; // 接管资源 other.data = nullptr; // 清空对方 cout << "移动构造\n"; } // 拷贝赋值 Box& operator=(const Box& other) { cout << "拷贝赋值\n"; if (this == &other) return *this; delete data; data = new int(*other.data); return *this; } // 移动赋值 Box& operator=(Box&& other) { cout << "移动赋值\n"; if (this == &other) return *this; delete data; // 释放旧资源 data = other.data; // 接管资源 other.data = nullptr; // 清空对方 return *this; } ~Box() { delete data; cout << "析构\n"; } }; Box createBox() { Box b(10); return b; } int main() { cout << "=== 移动构造示例 ===\n"; Box a = createBox(); // 触发移动构造(或RVO) cout << "=== 移动赋值示例 ===\n"; Box b(1); b = createBox(); // 触发移动赋值 }

五、可能看到的输出

不同编译器略有差异,常见输出:

=== 移动构造示例 === 构造 移动构造 析构 析构 === 移动赋值示例 === 构造 构造 移动赋值 析构 析构

有时你只会看到:

构造

那是RVO(返回值优化)在工作 ——
编译器直接在目标位置构造对象,连移动都省掉了。

六、移动构造 vs 移动赋值区别

类型场景是否已有资源核心动作
移动构造新对象创建没有直接接管资源
移动赋值对象已存在

先释放再接管

七、为什么要把对方清空?

other.data = nullptr;

原因:

不清空 → 析构 double free 清空 → delete nullptr 安全

八、现实类比

拷贝构造 = 复印整箱书 移动构造 = 把箱子搬走 拷贝赋值 = 先扔旧书再复印新书 移动赋值 = 先扔旧书再搬新箱子

九、终极锚点总结

拷贝构造 → 复制资源 移动构造 → 搬资源给新对象 拷贝赋值 → 复制资源给已有对象 移动赋值 → 清空自己再搬资源

移动语义不是复杂语法,
它是“拷贝太贵”被逼出来的性能进化。

当你理解:

资源复制 vs 资源转移

移动构造和移动赋值就再也不会混了。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/5 8:57:11

设计家用灭火器检查提醒工具,录入灭火器位置,有效期,每月提醒检查压力,外观,到期提醒更换,避免紧急情况无法使用。

1. 实际应用场景描述 场景&#xff1a; 小王家里和车库各有一个灭火器&#xff0c;但他经常忘记检查压力表指针是否在绿色区域&#xff0c;也记不清灭火器的生产日期和有效期。某次厨房小火灾时&#xff0c;发现灭火器已经过期&#xff0c;险些酿成大祸。 目标&#xff1a; 通过…

作者头像 李华
网站建设 2026/6/5 10:48:26

7.4 Kubernetes存储故障排查:PV挂载失败、存储类问题诊断技巧

7.4 Kubernetes存储故障排查:PV挂载失败、存储类问题诊断技巧 引言 存储问题是Kubernetes集群的常见问题。通过系统化的排查方法,可以快速定位和解决存储故障。本文将详细介绍存储故障排查的技巧。 一、PV挂载失败 1.1 检查PV/PVC # 查看PV kubectl get pv# 查看PVC kub…

作者头像 李华
网站建设 2026/6/5 9:38:35

大模型Agent Skills学习路线:从技能市场到数据预测,一篇搞定

文章介绍了Agent Skills的概念、学习资源和使用方法&#xff0c;特别是如何通过技能市场获取趋势预测技能并应用于数据分析。Skills本质是结构化、可复用的"高级使用说明书"&#xff0c;指导大模型按特定顺序调用工具处理问题。提供了多个学习网站和资源链接&#xf…

作者头像 李华
网站建设 2026/6/7 3:05:23

使用darknet detector train cfg/voc.data cfg/yolov3-voc.cfg darknet53.conv.74训练图片是怎么生成权重文件的,怎么定义权重文件名?

&#x1f3c6;本文收录于 《全栈 Bug 调优&#xff08;实战版&#xff09;》 专栏。专栏聚焦真实项目中的各类疑难 Bug&#xff0c;从成因剖析 → 排查路径 → 解决方案 → 预防优化全链路拆解&#xff0c;形成一套可复用、可沉淀的实战知识体系。无论你是初入职场的开发者&…

作者头像 李华