首页 > 解决方案 > 删除包含相同对象的单独链接列表时避免双重 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() 错误,但这可能会在以后造成内存泄漏。

我的实际代码比这要复杂得多,但我试着做一个简化的例子。不知道如何避免这种情况。

标签: cmemory-managementlinked-list

解决方案


只需使用引用计数。每当将新所有者添加到对象时,必须将 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.


推荐阅读