移动语义与移动构造
你可以把右值想成"临时工":事情做完就要下班了。这个时候如果还让它把手里的资料整份复印一遍再交接,就很浪费。
于是 C++11 做了一件事:引入 T&& 来接住右值/将亡值。
既然都要退场了,那就别复制了,直接把资源交接过去就行。
这一步,就是我们说的移动语义。
再往下就很自然了:语言里专门给了三件工具来做这件事,分别是移动构造、移动赋值和 std::move。
所以这章其实不是突然冒出来的新知识,而是上一章"右值引用"顺着往下长出来的结果。
- 结论
移动语义的核心不是"复制资源",而是"转移资源所有权"。
所以它可以在很多场景下避免深拷贝,提高性能。
- 移动构造函数
移动构造函数用于"用右值初始化新对象"。
常见写法是接管对方资源,然后把源对象置为可析构状态。
cpp
class MyString {
public:
char* data{};
MyString(const char* s = "") {
size_t n = std::strlen(s);
data = new char[n + 1];
std::memcpy(data, s, n + 1);
}
MyString(MyString&& other) noexcept
: data(other.data) {
other.data = nullptr;
}
~MyString() {
delete[] data;
}
};
- 移动赋值函数
移动赋值用于"把一个右值赋给已存在对象"。
要先释放自己原有资源,再接管对方资源,同时处理自赋值保护。
cpp
MyString& operator=(MyString&& other) noexcept {
if (this != &other) {
delete[] data; // 先释放旧资源
data = other.data; // 接管新资源
other.data = nullptr; // 源对象置空
}
return *this;
}
std::move的作用
std::move(x) 不会移动任何数据,它只是把 x 转成右值表达式。
真正是否移动,取决于类型是否实现了移动构造/移动赋值。
- 拷贝与移动对比
cpp
std::string s1 = "hello";
std::string s2 = s1; // 拷贝:复制内容
std::string s3 = std::move(s1); // 移动:转移资源
注意:被 move 之后的对象仍然"有效",但其值处于"未指定"状态。
通常只对它做重新赋值、销毁等安全操作,不依赖原内容。