c - 删除包含相同对象的单独链接列表时避免双重 free() 错误
问题描述
我正在做一个个人项目并且遇到了一个概念问题。我应该如何删除多个动态创建的链接列表,这些链接列表可能引用同一个动态创建的对象?
我有一些函数返回一个动态创建的链接列表,其中包含动态创建的对象。
List* generate_list() {
Object* object_address = create_object();
List* list = create_list();
list_append_end(list, object_address);
return list;
}
我有另一个函数,它删除链表并通过提供删除函数递归地删除存储在链表中的对象。
List* list = generate_list();
delete_list(list, delete_object_function);
现在,当我尝试删除包含对同一对象的引用的多个链表时,我遇到了这种方法的问题。
Object* object_address = create_object();
List* list = create_list();
List* another_list = create_list();
list_append_end(list, object_address);
list_append_end(another_list, object_address);
delete_list(list, delete_object_function);
delete_list(another_list, delete_object_function); // Double free() would occur here
我显然可以不删除两个列表之一以避免双重 free() 错误,但这可能会在以后造成内存泄漏。
我的实际代码比这要复杂得多,但我试着做一个简化的例子。不知道如何避免这种情况。
解决方案
只需使用引用计数。每当将新所有者添加到对象时,必须将 refcouter 增加get_object()
. 当通过减少计数器释放所有权时。put_object()
仅当计数器达到时释放对象0
。
struct {
...
int refcount;
} Object;
Object* create_object(void) {
...
obj->refcount = 1;
return obj;
}
Object* get_object(Object* obj) {
assert(obj->refcount > 0);
obj->refcount++;
return obj;
}
void put_object(Object* obj) {
assert(obj->refcount > 0);
obj->refcount--;
if (obj->refcount == 0) {
delete_object(obj);
}
}
它是安全的,因为get_object()
只能在调用者拥有对象时调用。因此对象refcount
将是非零的。
当 refcount 达到时0
,就没有对象的其他所有者了,所以没有人可以调用get_object()
.
现在用法可能如下所示:
Object* obj = create_object();
List* list = create_list();
List* another_list = create_list();
// let `list` own the object
list_append_end(list, get_object(obj));
// let `another_list` own the object as well
list_append_end(another_list, get_object(obj));
delete_list(list, put_object);
delete_list(another_list, put_object);
put_object(obj); // release ownership because `obj` goes out of scope
请注意,最后三个操作可以按任意顺序执行。
顺便提一句。NULL
在释放所有权后设置一个指向对象的指针是一个很好的做法。
在原始generate_list()
函数中,不需要添加额外的get_object
/put_objects
调用,因为指向的对象的所有权object_address
可以隐式地从变量传递object_address
给返回的list
.
推荐阅读
- sql - SQL 查找过去 30 天的记录计数,按以下方式分组
- html - 使用 62.5% 作为 html root 使用 rem 真的安全吗?这是否意味着 100% 的浏览器默认字体大小为 16 像素?
- sql - 错误:关系“vinyls”不存在,但在 psql 中有效
- python - 将两个参数传递到 url 元素中,同时运行循环以获取 webscrape 数据
- android - Android 属性仅适用于样式?
- android - androids studio sqlite 问题与 selectionArgs 参数
- java - 如何使用java代码在资产中动态创建JSON文件或使用android中的java代码更新现有的json文件
- python-3.x - 如何根据另一列的某些条件在数据框中添加列?
- python - 从 cookie 中提取 Httponly、Secure、域和路径
- database - 如何从远程 TimescaleDb 中删除最旧的条目,维护数据库的完整本地备份?