我见过很多C库,它们没有将它们在内部处理的对象作为不同的类型呈现,而是在让你访问它们之前用void指针包装它们。在实践中,“私有”源文件看起来像:
typedef struct {
int value;
} object;
void * object_new(void)
{
object *o = malloc(sizeof(object));
o->value = 1;
return o;
}
int object_get(void *o)
{
return (object *)o->value;
}
void * object_free(void *o)
{
free(o);
}
在主标题中你只有:
void * object_new(void);
int object_get(void *o);
void * object_free(void *o);
现在我想知道:他们这样做有什么特别的原因吗?如果想要确保API用户无法访问对象的内部,仅仅在主库头中公开类型名称并隐藏底层结构的详细信息(或者其他任何内容)是不够的。实际文件中的实际对象?
答案 0 :(得分:5)
隐藏void
指针后面的类型的原因可能是(误导)企图隐藏(modular programming)内部细节。这很危险,因为它抛出了编译器可能会在窗口外执行的任何类型检查。
更好的事情就是这样:
for-user.h
:
struct internalstuff;
void somefunc(struct internalstuff *p);
for-internal-use.h
:
#include "for-user.h"
struct internalstuff { ...};
implementation.c
:
#include "for-internal-use.h";
void somefunc(struct internalstuff *p)
{
...
}
这样,没有人会将internalstuff
与随机字符串或malloc(3)
的原始结果混淆,而不会至少收到警告。只要你在C中只提到指向struct internalstuff
的指针就可以了,不要手头有struct
的定义。
可以使用class
在C ++中完成同样的事情,如果Objective C不允许这样做,我会感到很惊讶。但面向对象编程语言有自己的,更灵活的工具。在那里,您可以定义要导出的简单基类,同时使用内部扩展。看一下好的C ++书籍了解详情(这里有广泛的列表)。
答案 1 :(得分:1)
在一个对象世界(Obj-C和C ++)中,我认为其原因主要与继承有关。如果从基类创建子类,则在创建类的新实例时,返回值的类型没有问题。由于没有显示内部细节或创建依赖关系,因此只有直接C,似乎没有明确的原因。
答案 2 :(得分:1)
你是对的..大多数情况下的想法是限制API用户从对象的内部。关于类型名称的决定实际上只是风格问题。如果您按照建议(某些API执行)在标题中公开类型名称,它可能看起来像:
typedef void* object;
从编译器的角度来看,没有真正的优点或缺点。虽然它确实让API用户更好地了解正在发生的事情:
object object_new(void);
int object_get(object o);
void object_free(object o);