删除包含相同对象的单独链接列表时避免双重 free() 错误

Avoiding a double free() error when deleting separate Linked Lists that contain the same object

我正在做一个个人项目,运行 遇到了一个概念性问题。我应该如何删除多个动态创建的链表,这些链表可能引用同一个动态创建的对象?

我有一些功能,returns 一个动态创建的链接列表,其中包含动态创建的对象。

List* generate_list() {
  Object* object_address = create_object();
  List* list = create_list();
  list_append_end(list, object_address);
  return list;
}

我还有一个删除链表的功能,通过提供delete函数递归删除链表内部存储的对象

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.

时才减少 counter.Free 对象
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 将是非零的。

当引用计数达到 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.