移动语义的核心是资源移交而非复制,需正确声明noexcept移动操作、识别隐式移动时机、慎用std::move,并确保移动前提(如禁用或默认拷贝操作)到位。

移动语义的核心目标,是用“搬走资源”代替“复制资源”,从而避免深拷贝开销。关键不在于写几个 std::move,而在于识别可移动的右值、正确声明移动构造函数/移动赋值运算符,并让编译器有机会自动调用它们。
识别并利用隐式移动的时机
编译器在特定场景下会自动选择移动而非拷贝,前提是类提供了合规的移动操作:
- 函数返回局部对象(如
return std::string("hello");)——触发返回值优化(RVO)或自动移动(C++17起 guaranteed copy elision + move fallback) - 用临时对象初始化同类型对象(如
std::vector)——若v = get_temp_vector(); get_temp_vector()返回右值,且vector有移动构造函数,则调用移动而非拷贝构造 - 容器插入临时元素(如
vec.push_back(std::string("tmp"));)——push_back的右值重载会调用移动构造
正确实现移动操作: noexcept + 资源接管 + 自赋值安全
移动构造函数和移动赋值运算符不是“更快的拷贝”,而是“资源移交协议”:
- 务必声明为
noexcept:否则标准容器(如std::vector在扩容时)可能拒绝使用移动,退回到拷贝 - 移动后原对象必须处于“有效但未指定状态”:指针置空、长度置 0、不释放已移交内存;不能析构已被移走的资源
- 移动赋值需处理自赋值(虽罕见,但
obj = std::move(obj);合法),通常用“先移后清”或交换惯用法(swap(*this, other);)
谨慎使用 std::move:它不移动,只转换类型
std::move(x) 只是把 x 强转为右值引用,不执行任何移动动作。滥用会导致意外移动或重复移动:
立即学习“C++免费学习笔记(深入)”;
- 对具名变量(左值)调用
std::move后,该变量不再可用——后续访问是未定义行为 - 不要对函数参数(即使声明为
T&&)无条件调用std::move:万一是从左值传入的转发引用(universal reference),应优先用std::forward - 仅在明确要“放弃所有权”时使用:如将局部资源交出、转移给成员变量、或作为函数右值参数传递
移动语义生效的前提:别让拷贝构造“拦路”
即使写了移动操作,如果类还存在用户定义的拷贝构造函数或拷贝赋值运算符,编译器不会自动生成移动操作(C++11/14)。必须显式声明或 = default:
- 若未禁用拷贝(如未删除
const T&构造),且未提供移动操作,编译器只用拷贝——性能白丢 - 若希望只支持移动不支持拷贝(如
std::unique_ptr),需显式= delete拷贝操作,并= default移动操作 - C++17 起,若类满足“可平凡移动”条件(如仅含 trivial 成员),即使没写
= default,也可能被隐式声明为 defaulted;但仍建议显式写出以明确意图











