到目前为止,我一直在使用void *
作为在C中封装私有数据的方法。这个想法是:用户不应该打扰内部,只需要请求公开的函数。
因此例如:
typedef void* myPrivateType;
myPrivateType createMPt(int someArg);
int doSomething(myPrivateType mpt, int someOtherArg);
void freeMpt(myPrivateType mpt);
这可以很好地隐藏myPrivateType
的内部。但是,还有一个最后的小问题:void*
是如此宽松,编译器将默默地接受任何类型的指针,并且在类型不正确的情况下不会触发任何警告。
这看起来像一个小问题,但它只会增加用户不正确地使用界面的可能性,并且会花费大量时间来调试错误。
因此,我现在倾向于使用incomplete types而不是void*
,这是在编译期间控制类型的更严格方法。
因此前一个例子如下:
typedef struct foo* myPrivateType;
myPrivateType createMPt(int someArg);
int doSomething(myPrivateType mpt, int someOtherArg);
void freeMpt(myPrivateType mpt);
正如您所看到的,几乎没有任何变化,只有typedef。它比以前的例子效果更好,如果用户提供的另一个指针不是“myPrivateType”,编译器会抱怨,错误将立即被捕获。
这是一个相当不错的选择。除此之外,在代码的“私有部分”(.c
文件)中,我将必须定义struct foo
是什么。在某些情况下,当这些内容明确而静态地定义时,它非常简单。
但有时候,myPrivateType
的内容取决于运行时提供的某些变量,因此其大小可能会有所不同。这对于C struct
没有好处,它应该在编译时具有定义的大小。
作为一种解决方法,我可以通过这种方式输入typedef myPrivateType:
typedef size_t* myPrivateType;
或
typedef size_t myPrivateType[]; // equivalent
这样可以稍后决定大小,作为size_t
的倍数。但现在,myPrivateType
更宽松,因为任何size_t*
指针也适合该法案。
我想知道是否有一种结合两种属性的方法,myPrivateType
非常严格,因此不可能与任何其他类型的指针混淆,但底层私有数据保持选择其大小的能力在运行时。
答案 0 :(得分:1)
怎么样:
struct myPrivateType { void * private; };
使用方式与使用原始void指针的方式完全相同。您可以在标题中公开整个结构,因为应用程序无论如何都无法使用它包含的void指针。
这解决了使用void *进行隐式转换的问题,而不会在库函数中的mpt->私有解除引用之外引入任何烦恼。
编辑:如果您愿意,可使用typedef struct myPrivateType myPrivateType;
构造。
答案 1 :(得分:1)
我想知道是否有办法将两个属性组合在一起,myPrivateType非常严格,因此无法与任何其他类型的指针混淆,但底层私有数据保留了在运行时选择其大小的能力
是的,您可以使用以下方法轻松完成:
struct foo
{
int dataType;
void* fooData;
};
struct FooData1
{
// ...
};
struct FooData2
{
// ...
};
myPrivateType createMPt(int someArg)
{
myPrivateType ret = malloc(sizeof(*ret));
if ( someArg == 10 )
{
ret->dataType = 1;
ret->fooData = malloc(sizeof(FooData1));
}
else if ( someArg == 20 )
{
ret->dataType = 2;
ret->fooData = malloc(sizeof(FooData2));
}
else
{
// ...
}
}
<强>更新强>
如果一个公共函数,一个在.h文件中声明的函数,在紧密循环中被多次调用,并且该函数的实现必须基于dataType
{{1}调用不同的函数。你可以在struct foo
中存储一个函数指针。这类似于在C ++类中使用虚拟表。
struct foo