实现这一目的的规范方法是使用不透明指针和公共结构体,以及分配器、获取器和设置器来处理私有元素。大致如下:
foo.h
typedef struct Foo {
} Foo;
Foo *new_Foo(void);
void Foo_something_opaque(Foo* foo);
foo.c
#include "foo.h"
typedef struct Private_Foo_ {
struct Foo foo;
} Private_Foo_;
Foo *new_Foo(void)
{
Private_Foo_ *foo = malloc(sizeof(Private_Foo_));
return (Foo*) foo;
}
void Foo_something_opaque(Foo *foo)
{
Private_Foo_ *priv_foo = (Private_Foo_*) foo;
}
这个方法可行是因为C语言保证了结构体变量的地址始终等于第一个结构体元素的地址。我们可以利用这一点,在私有Foo_结构体中包含一个公共的Foo,将整个结构体的指针分配出去,编译单元无法访问Private_Foo_结构体定义,只能看到一些没有上下文的内存。
需要注意的是,C++在幕后工作方式相当类似。
更新
正如KereekSB所指出的那样,如果在数组中使用它,它会破坏它。
我的建议是:不要创建
Foo f[]
,虽然很诱人,而是创建指向Foo的指针数组:
Foo *f[]
。
如果非要在数组中使用它,请执行以下操作:
foo_private.h
typedef struct Private_Foo_ {
/* private elements */
} Private_Foo_;
static size_t Private_Foo_sizeof(void) { return sizeof(Private_Foo_); }
foo_private.h被编写成可以编译为对象文件的方式。使用一些辅助程序链接并使用Private_Foo_sizeof()
的结果从某个foo.h.in文件生成实际的、平台相关的foo.h。
foo.h
#include
#define FOO_SIZEOF_PRIVATE_ELEMENTS <generated by preconfigure step>
typedef struct Foo_ {
char reserved[FOO_SIZEOF_PRIVATE_ELEMENTS];
} Foo;
Foo *new_Foo(void);
void Foo_something_opaque(Foo* foo);
foo.c
#include "foo.h"
#include "foo_private.h"
Foo *new_Foo(void)
{
Foo *foo = malloc(sizeof(Foo));
return (Foo*) foo;
}
void Foo_something_opaque(Foo *foo)
{
Private_Foo_ *priv_foo = (Private_Foo_*) foo.reserved;
}
个人认为这真的很混乱。虽然我喜欢智能容器(不幸的是,C没有标准容器库)。无论如何:在这样一个容器中,它通过像函数一样创建
Array *array_alloc(size_t sizeofElement, unsigned int elements);
void *array_at(Array *array, unsigned int index);
/* and all the other functions expected of arrays */
可以参考libowfaw的实现示例。对于类型Foo,提供一个函数非常容易。
Array *Foo_array(unsigned int count);