移动构造与析构顺序
为了理解std::move在干什么,写了一个小例子研究一下
std::move(obj1)
不会真的移动数据,它只是把 obj1
转成右值引用(MyClass&& obj1)
,让编译器选择调用 MyClass 的移动构造函数。
移动构造函数会接管资源的指针(例如堆内存地址)而不是复制数据,这样速度更快。
移动后,obj1 处于有效但未指定的状态(通常是空的,但不要依赖它的具体内容)。
#include <iostream>
#include <utility>
#include <string>
class MyClass {
private:
int* data;
std::string name; // 记录对象名字,方便输出
public:
MyClass(int value, std::string n)
: data(new int(value)), name(std::move(n)) {
std::cout << name << " 构造函数: 分配数据 " << *data << "\n";
}
// 拷贝构造
MyClass(const MyClass& other)
: data(new int(*other.data)), name(other.name + "_copy") {
std::cout << name << " 拷贝构造: 复制数据 " << *data << "\n";
}
// 移动构造
MyClass(MyClass&& other) noexcept
: data(other.data), name(other.name + "_moved") {
other.data = nullptr;
std::cout << name << " 移动构造: 接管数据\n";
}
~MyClass() {
if (data) {
std::cout << name << " 析构函数: 释放数据 " << *data << "\n";
delete data;
} else {
std::cout << name << " 析构函数: 空数据,无需释放\n";
}
}
};
int main() {
MyClass obj1(42, "obj1"); // 第一个创建
MyClass obj2 = obj1; // 第二个创建(拷贝构造)
MyClass obj3 = std::move(obj1); // 第三个创建(移动构造)
return 0;
}
运行示例输出
obj1 构造函数: 分配数据 42
obj1_copy 拷贝构造: 复制数据 42
obj1_moved 移动构造: 接管数据
obj1_moved 析构函数: 释放数据 42
obj1_copy 析构函数: 释放数据 42
obj1 析构函数: 空数据,无需释放
重点说明
- 拷贝构造(MyClass(const MyClass&))
- 分配新内存并复制数据。
- 比较慢,因为要分配+复制。
- 移动构造(MyClass(MyClass&&))
- 接管旧对象的指针,不分配新内存。
- 原对象的指针置空,防止析构时重复释放。
- 快很多,尤其是资源很大时(例如大数组、文件句柄、网络连接等)。
std::move
不是真的“移动”,它只是把左值强制转成右值引用,触发调用移动构造函数。
在这个例子里,obj1
、obj2
、obj3
是 main 函数的局部变量,它们的析构顺序遵循 C++ 的作用域规则——后创建的对象先析构(栈是后进先出 LIFO)。