如何强制std::vector使用移动构造函数而非复制构造函数?
首先,咱们来分析你遇到的编译错误:error: use of deleted function ‘TestData::TestData(const TestData&)。这是因为你在add函数里调用vector.push_back(d)时,d是一个左值(所有命名的对象都是左值),而push_back的左值重载版本会尝试调用拷贝构造函数——但你已经显式删除了拷贝构造,所以编译器直接报错。
要强制编译器使用移动构造,你需要让push_back接收到一个右值(或者右值引用),这里有几种可行的方案:
方案1:用std::move将左值转为右值引用
修改add函数中的push_back调用,把d通过std::move转换成右值引用:
void add(std::vector<TestData> &vector) { TestData d; d.x = 1; d.ptr = new char[12]; memcpy(d.ptr, "Test string", 11); vector.push_back(std::move(d)); // 这里用std::move触发移动构造 }
std::move并不会真的“移动”数据,它只是一个类型转换工具,告诉编译器:“这个对象的资源可以被转移,不用保留原状态”。这样push_back就会调用接受右值引用的重载,进而触发你的移动构造函数。
方案2:直接构造临时对象(自动成为右值)
你也可以直接在push_back参数里构造临时的TestData对象,临时对象本身就是右值,会自动触发移动构造:
void add(std::vector<TestData> &vector) { TestData d; d.x = 1; d.ptr = new char[12]; memcpy(d.ptr, "Test string", 11); vector.push_back(std::move(d)); // 或者直接构造临时对象(写法示例): // TestData temp; // temp.x = 1; // temp.ptr = new char[12]; // memcpy(temp.ptr, "Test string", 11); // vector.push_back(std::move(temp)); }
关键修正:别用默认的移动构造!
你当前代码里用了=default的移动构造函数,这会导致双重释放的严重问题!默认的移动构造只是简单地逐成员拷贝值——对于指针ptr来说就是浅拷贝,移动之后原对象d的ptr仍然指向原来的内存地址。当add函数结束时,d被析构,会delete[] ptr;之后main函数结束时,vector里的TestData对象析构时又会delete[] ptr,这就造成了双重释放,程序必然崩溃。
所以你必须自己实现移动构造和移动赋值函数,把原对象的ptr置为nullptr:
struct TestData { TestData(const TestData&) = delete; TestData& operator=(const TestData&) = delete; // 自定义移动构造函数 TestData(TestData &&other) noexcept : x(other.x), ptr(other.ptr) { other.ptr = nullptr; // 把原对象的指针置空,避免它析构时释放内存 std::cout << "TestData(TestData &&)" << std::endl; } // 自定义移动赋值函数 TestData& operator=(TestData &&other) noexcept { if (this != &other) { // 先释放当前对象的资源 if (ptr != nullptr) delete[] ptr; // 转移资源 x = other.x; ptr = other.ptr; other.ptr = nullptr; } std::cout << "TestData& operator=(TestData &&)" << std::endl; return *this; } TestData() { std::cout << "TestData()" << std::endl; } ~TestData() noexcept { if(ptr != nullptr) delete []ptr; // 修正:null不是C++关键字,改用nullptr std::cout << "~TestData(), ptr = " << (ptr == nullptr ? "nullptr" : "not null") << std::endl; } int x; char *ptr = nullptr; };
这样移动之后,原对象的ptr变成nullptr,它的析构函数就不会释放内存,只有vector里的对象会负责释放资源,彻底避免了双重释放的问题。
另外,你析构函数里的if(ptr != null)是错误的,null不是C++的关键字,必须改成nullptr,否则也会编译报错。
内容的提问来源于stack exchange,提问作者folibis

