1

在 C 中,如果您想拥有通用容器,一种流行的方法是使用void*. 如果通用容器包含一些具有自己的释放函数的自定义结构,它可能会要求该函数:

struct Foo {...};
Foo *Foo_Allocate(...);
void Foo_Deallocate(const Foo*);


int main(void)
{
    /* Let's assume that when you create the list you have to
       specify the deallocator of the type you want to hold */
    List *list = List_Allocate(Foo_Deallocate);

    /* Here we allocate a new Foo and push it into the list.
       The list now has possession of the pointer. */
    List_PushBack(list, Foo_Allocate());

    /* When we deallocate the list, it will also deallocate all the
       items we inserted, using the deallocator specified at the beginning */
    List_Deallocate(list);
}

但很可能释放器函数的类型需要一个void*

typedef void (*List_FnItemDeallocator)(const void*);

问题是Foo_Deallocate需要 a const Foo*,而不是 a const void*。即使它们的签名不匹配,传递函数是否仍然安全?可能不是,因为指针类型在 C 中不一定是相同的大小。

如果这不可能,那么让所有释放器函数采用 aconst void*而不是指向它们相关类型的指针是否是个好主意,以便它们与通用容器兼容?

4

2 回答 2

0

正如您所说,将指针分配给不同的函数类型是无效的。

您应该将 avoid*作为参数,并在每个函数内部执行一些检查,以查看给定指针是否与预期类型匹配(例如检查结构开头的幻数)。

于 2012-05-02T17:07:01.523 回答
0

如前所述,您可以使用幻数或“标题”来指定析构函数。您可以使用此标头走得很远,甚至可以选择一个“众所周知的、已注册的”释放器(在这种情况下,您实际上不需要存储函数指针,可能只是将整数索引存储到数组中),或者有一个 '标头中的 flags 部分,指定它包含一个“扩展”释放器。可能性很远,很有趣。

所以你的列表“标题”看起来像这样

#define LIST_HEAD struct list *next; struct list *prev; short flags;
struct list { LIST_HEAD };
struct list_with_custom_deallocator { LIST_HEAD void (*dealloc)(void*); };

现在,实际上回答了您的问题.. 为什么不定义一个通用的标头类型,并让您的释放器获取指向类型的指针(在我的示例中为 a struct list*),然后将其转换为任何特定的相关类型-甚至更好,也许启发式地确定实际的结构和释放flags器(如 Binyamin 所暗示的)。

于 2012-05-02T17:19:31.743 回答