为何在C++的destructor中调用set的erase会导致程序崩溃?
问题分析与解决
崩溃核心原因:局部对象销毁顺序导致悬空指针访问
main函数中,局部对象e1(record类型)先声明,n1(file类型)后声明。C++规定局部变量的销毁顺序为逆序声明顺序——即程序退出main时,先销毁n1,再销毁e1。当e1的析构函数执行并遍历file_log中的&n1时,n1已经被销毁,此时访问已销毁对象的ptr_to_record成员属于未定义行为,直接触发程序崩溃。次要问题:双向关联未正确建立
你的代码仅将n1的指针插入e1的file_log,但未将e1的指针插入n1的ptr_to_record,导致n1.ptr_to_record实际为空。你观察到的“大小为1”应为测试误判或代码遗漏了关联步骤,即便销毁顺序正确,调用remove时尝试erase不存在的元素也属于无效操作。修复方案
- 调整局部对象声明顺序:让
n1先声明、e1后声明,确保e1析构时n1仍处于有效状态。 - 补全双向关联逻辑:封装关联函数,确保插入
file_log的同时,将record指针同步插入对应file的ptr_to_record。 - 可选优化:复杂场景下可使用
std::shared_ptr/std::weak_ptr管理对象生命周期,规避手动指针管理的风险。
- 调整局部对象声明顺序:让
修复后的代码示例:
#include <iostream> #include <memory> #include <set> class file; class record { public: ~record() { deletefun(); } int x; std::set<file*> file_log; void deletefun(); // 封装双向关联函数 void bind_file(file* f) { file_log.insert(f); f->ptr_to_record.insert(this); } }; class file { public: std::set<record*> ptr_to_record; void remove(record* e) { std::cout << e->x << std::endl; // 先查找再删除,避免无效操作 auto iter = ptr_to_record.find(e); if (iter != ptr_to_record.end()) { ptr_to_record.erase(iter); } } }; void record::deletefun() { for (auto f_ptr : file_log) f_ptr->remove(this); } int main() { // 调整声明顺序,确保e1先于n1销毁 file n1; record e1; e1.x = 10; // 建立双向关联 e1.bind_file(&n1); }
内容的提问来源于stack exchange,提问作者cheng
相关产品推荐
相关产品推荐

