You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何在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不存在的元素也属于无效操作。

  • 修复方案

    1. 调整局部对象声明顺序:让n1先声明、e1后声明,确保e1析构时n1仍处于有效状态。
    2. 补全双向关联逻辑:封装关联函数,确保插入file_log的同时,将record指针同步插入对应file的ptr_to_record。
    3. 可选优化:复杂场景下可使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 05:33:37