C++错误咨询:移动构造函数中使用已删除函数问题
Hey 伙计,咱们来拆解你碰到的这个移动构造函数相关的错误。先理清楚核心原因,再给你靠谱的解决办法:
错误根源
C++11及以后的标准里有个关键规则:当你显式定义了拷贝构造函数、拷贝赋值运算符、析构函数中的任意一个,编译器就会自动删除默认生成的移动构造函数和移动赋值运算符。
看你的代码,你已经写了Container(const Container & obj)这个拷贝构造函数(虽然没写完),所以编译器直接把默认的移动构造给禁用了。这时候如果你的代码里有需要触发移动构造的场景(比如用std::move转移对象,或者std::vector的扩容操作自动触发移动),编译器找不到可用的移动构造函数,就会抛出「使用已删除函数」的错误。
另外补充一句:你的类里用了裸指针int* m_Data,就算编译器没删移动构造,默认生成的版本也会做浅拷贝,很容易导致重复释放内存的问题——不过这是另一个坑,咱们先解决眼前的错误。
解决办法
针对这个问题,有两种主流方案,根据你的需求选择:
方案1:手动实现移动构造和移动赋值(推荐)
既然编译器把默认的删了,那咱们自己写一个符合语义的移动构造函数,直接转移资源而不是拷贝,既高效又安全:
// 移动构造函数,用noexcept告诉编译器不会抛出异常(容器操作会更高效) Container(Container&& obj) noexcept : m_Data(obj.m_Data) { // 把源对象的指针置空,避免它析构时释放已经转移的资源 obj.m_Data = nullptr; std::cout << "Move Constructor: Transferring resource" << std::endl; }
同时建议配套实现移动赋值运算符,遵循C++的「五法则」:
Container& operator=(Container&& obj) noexcept { if (this != &obj) { // 防止自赋值 // 先释放当前对象的资源 delete[] m_Data; // 转移源对象的资源 m_Data = obj.m_Data; obj.m_Data = nullptr; std::cout << "Move Assignment: Transferring resource" << std::endl; } return *this; }
方案2:让编译器恢复默认移动构造(谨慎使用)
如果你确定代码里不会用到移动语义,或者想依赖编译器生成默认版本(但注意裸指针的浅拷贝风险),可以显式要求编译器生成:
// 显式声明默认移动构造和移动赋值 Container(Container&&) = default; Container& operator=(Container&&) = default;
⚠️ 注意:这个方案只适合你能完全控制对象的生命周期,不会出现浅拷贝导致的double free问题的场景,一般更推荐方案1。
额外补充:完善你的拷贝构造函数
你的代码里拷贝构造函数的for循环没写完,记得补全深拷贝逻辑,否则会有未定义行为:
Container(const Container & obj) { m_Data = new int[20]; // 补全拷贝循环,完成深拷贝 for (int i = 0; i < 20; ++i) { m_Data[i] = obj.m_Data[i]; } std::cout << "Copy Constructor: Deep copying 20 int" << std::endl; }
完整修正后的代码示例
#include<iostream> #include<vector> class Container { int * m_Data; public: Container() { m_Data = new int[20]; std::cout << "Constructor: Allocation 20 int" << std::endl; } ~Container() { if (m_Data) { delete[] m_Data; m_Data = nullptr; // 用nullptr代替NULL更符合C++11+规范 } std::cout << "Destructor called" << std::endl; } // 深拷贝构造函数 Container(const Container & obj) { m_Data = new int[20]; for (int i = 0; i < 20; ++i) { m_Data[i] = obj.m_Data[i]; } std::cout << "Copy Constructor: Deep copying 20 int" << std::endl; } // 拷贝赋值运算符 Container& operator=(const Container& obj) { if (this != &obj) { delete[] m_Data; m_Data = new int[20]; for (int i = 0; i < 20; ++i) { m_Data[i] = obj.m_Data[i]; } std::cout << "Copy Assignment: Deep copying 20 int" << std::endl; } return *this; } // 移动构造函数 Container(Container&& obj) noexcept : m_Data(obj.m_Data) { obj.m_Data = nullptr; std::cout << "Move Constructor: Transferring resource" << std::endl; } // 移动赋值运算符 Container& operator=(Container&& obj) noexcept { if (this != &obj) { delete[] m_Data; m_Data = obj.m_Data; obj.m_Data = nullptr; std::cout << "Move Assignment: Transferring resource" << std::endl; } return *this; } }; // 测试移动语义 int main() { Container c1; Container c2 = std::move(c1); // 触发移动构造 Container c3; c3 = std::move(c2); // 触发移动赋值 return 0; }
内容的提问来源于stack exchange,提问作者Sourav

