删除包含相同对象的多个独立链表时如何避免double free()错误
解决方案
方案1:为动态对象引入引用计数(最通用)
这是C/C++场景下处理共享堆内存的常规方案,核心逻辑是给每个动态对象增加引用计数标记,只有计数归0时才真正释放对象。
改造步骤:
- 在
Object结构体中新增ref_count字段,对象创建时初始化为1 - 新增引用操作、释放引用操作两个辅助函数
typedef struct Object { // 原有对象字段保留 int ref_count; // 新增引用计数字段 } Object; // 原有对象创建函数修改 Object* create_object() { Object* obj = malloc(sizeof(Object)); // 原有初始化逻辑保留 obj->ref_count = 1; return obj; } // 新增:增加引用 void obj_ref(Object* obj) { if (obj != NULL) obj->ref_count++; } // 新增:释放引用,计数归0时真正释放对象 void obj_unref(Object* obj) { if (obj == NULL) return; obj->ref_count--; if (obj->ref_count == 0) { // 调用你原有对象释放逻辑 delete_object_function(obj); } }
- 链表操作适配规则:
- 往链表插入对象时,调用
obj_ref(待插入对象),标记链表持有该对象的一份引用 - 删除链表时,给
delete_list传入的释放函数替换为obj_unref,不要直接调用对象释放函数
改造后你原来的报错场景就可以正常运行:对象创建后计数为1,插入两个链表时各加1次计数变为3,删除第一个链表计数减为2,删除第二个链表计数减为1,你自己持有对象指针的逻辑如果不再需要该对象,再调用一次obj_unref计数就会归0自动释放,不会出现double free,也不会内存泄漏。
方案2:外置引用计数哈希表(无需修改原有对象结构)
如果你不能修改Object的结构体定义,可以用全局哈希表存储所有动态对象的地址和对应的引用计数,逻辑和方案1完全一致,只是把计数存在哈希表而非对象内部。
该方案的缺点是多线程场景下需要对哈希表加锁,性能略低于内置引用计数。
方案3:全局对象池统一回收(适合短生命周期程序)
如果你的程序运行周期短、对象总占用内存可控,可以引入全局对象池:所有create_object生成的对象都先注册到对象池中,所有链表删除时都不释放持有的对象,等程序即将退出前,统一遍历对象池释放所有对象。
该方案实现最简单,完全规避double free问题,适合小型工具类项目使用。
注意事项
- 引用计数方案需要*严格遵守「获取指针时加引用、放弃指针时减引用」*的约定,避免出现逻辑漏洞
- 如果存在对象间循环引用的场景,引用计数无法自动回收,需要手动打断循环引用关系,或改用标记清除式的内存回收逻辑
内容的提问来源于stack exchange,提问作者Biggs
相关产品推荐
相关产品推荐

