移动构造函数直接接管右值资源而不复制,拷贝构造函数则需重新分配并复制数据;调用取决于源是否为右值,如临时对象、字面量或std::move转换;未定义移动构造时退化为拷贝;移动后源对象处于有效但未指定状态,需手动置空指针以防double-free。

移动构造函数不会复制资源,而是直接接管右值对象的指针、句柄等资源;拷贝构造函数必须为新对象重新分配内存并逐字节复制数据——这是性能差异的根本来源。
什么时候调用移动构造而不是拷贝构造
编译器优先匹配 MyClass(MyClass&&),但前提是源对象是右值。常见右值场景包括:
- 函数返回临时对象:
MyClass func() { return MyClass(); }→MyClass a = func();(若未触发RVO) - 字面量或表达式结果:
MyClass b = MyClass("hello"); - 显式转换:
MyClass c = std::move(d);(d是左值,std::move强制转为右值引用)
注意:如果类没定义移动构造函数,右值也会退回到调用拷贝构造函数(只要它存在),但会失去性能优势。
参数签名和资源处理方式完全不同
MyClass(const MyClass&) 和 MyClass(MyClass&&) 是两个独立重载函数,不能互相替代。
- 拷贝构造函数参数是
const左值引用:保证不修改原对象,且能绑定所有左值(如变量a) - 移动构造函数参数是右值引用:只能绑定将亡值(xvalue)或纯右值(prvalue),且函数体内可安全“掏空”源对象(如把
other.ptr设为nullptr) - 基础类型(
int、double)在两种构造中都只是按位复制,无实际区别;真正差异体现在含动态资源的成员上(如char*、std::unique_ptr)
容易被忽略的关键细节
移动后源对象不是“销毁”,而是进入有效但未指定状态(valid but unspecified state)——它仍能被析构,但不能再被安全使用。
- 例如:移动一个
std::string后,原字符串可能变为空,也可能保留部分缓冲区,标准不保证;但调用.empty()或.size()是安全的 - 如果你自己写移动构造函数,必须手动置空所有已转移的指针/句柄,否则析构时会 double-free
- 移动操作不自动加
noexcept,但 STL 容器(如std::vector::push_back)在需要异常安全时会检查移动是否noexcept;建议显式标注:MyClass(MyClass&&) noexcept
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











