从C++ vector中删除含堆内存的自定义类对象时出现double free错误
错误原因
你的问题核心是自定义类违反了C++的三五法则,默认拷贝行为是浅拷贝,触发了重复释放内存的问题,具体逻辑如下:
- 你没有为类
B显式实现拷贝构造函数、拷贝赋值运算符,编译器会自动生成默认实现,默认实现只会逐字节复制对象的成员值:也就是会直接复制hehe指针本身的地址,不会为新对象申请独立的堆内存,这就是浅拷贝。 - 当你对存储了5个
B对象的vector执行erase删除下标2的元素时,vector会把下标3、4的元素向前移动到下标2、3的位置,移动过程会调用默认拷贝赋值运算符:- 下标3的对象赋值给下标2的位置后,两个对象的
hehe指针指向同一块堆内存 - 移动完成后,原来下标4的对象会被析构,释放它的
hehe指向的内存,这时新的下标3的对象的hehe已经变成野指针 - 后续整个
vector销毁、逐个析构元素时,野指针会被再次delete,就触发了double free错误。
- 下标3的对象赋值给下标2的位置后,两个对象的
为什么元素数为1时运行正常
当vector里只有1个元素,执行erase时不需要移动其他元素,直接析构这唯一的对象即可,全程不会触发浅拷贝,也就不会出现多个对象共享同一块堆内存的情况,所以不会报错。
解决方法
你可以选择以下任意一种方案修复问题:
方案1:显式实现深拷贝逻辑,符合三五法则
为类B补充实现拷贝构造、拷贝赋值运算符,保证每个对象的hehe指针指向独立的堆内存:
class B { public: std::string a; std::string b; int *hehe; B() { a = "Hello"; b = ", World!"; hehe = new int[7]; for (int i = 0; i < 7; ++i) { hehe[i] = i; } } // 拷贝构造 B(const B& other) { a = other.a; b = other.b; hehe = new int[7]; for (int i = 0; i <7; ++i) { hehe[i] = other.hehe[i]; } } // 拷贝赋值运算符 B& operator=(const B& other) { if (this == &other) return *this; // 释放原有内存 delete[] hehe; // 深拷贝新内容 a = other.a; b = other.b; hehe = new int[7]; for (int i = 0; i <7; ++i) { hehe[i] = other.hehe[i]; } return *this; } ~B() { if (hehe) delete[] hehe; } };
方案2:使用标准容器/智能指针代替裸指针,避免手动管理内存
这是更推荐的方案,不用手动管理内存,编译器默认生成的拷贝行为就符合预期:
#include <vector> class B { public: std::string a; std::string b; // 用std::vector代替裸数组,无需手动申请释放内存 std::vector<int> hehe; B() { a = "Hello"; b = ", World!"; hehe.resize(7); for (int i = 0; i < 7; ++i) { hehe[i] = i; } } // 不需要手动实现析构、拷贝构造、拷贝赋值,默认实现即可正常工作 };
内容的提问来源于stack exchange,提问作者BillyKlebitz
相关产品推荐
相关产品推荐

