将结构分区为私有和公共部分?

在C ++和Java中,数据结构可以包含privatepublicprotected区域。 我想将这个概念移植到我正在编写的C语言程序中。

是否有任何习惯用于在C struct实现私有或受保护的函数指针和数据字段? 我知道C struct是公共的,我正在寻找一个成语来帮助隐藏一些实现细节并强迫用户使用公共接口。

注意:该商店已选择该语言,因此我无法将面向对象的概念应用到C.

谢谢。

如你所知,你不能这样做。 然而,有些习语可以产生类似的效果。

C将允许你做一些类似于面向对象设计中所谓的“pimpl”习语。 您的结构可以有一个指向另一个前向声明的结构的不透明指针,该结构充当结构的私有数据。 在结构上运行的函数代替了成员函数,可以具有私有成员的完整定义,并且可以使用它,而代码的其他部分则不能。 例如:

在标题中,foo.h:

  struct FooPrivate; struct Foo { /* public: */ int x; double y; /* private: */ struct FooPrivate* p; }; extern struct Foo* Foo_Create(); /* "constructor" */ extern void Foo_DoWhatever(struct Foo* foo); /* "member function" */ 

在实现中,foo.c:

  struct FooPrivate { int z; }; struct Foo* Foo_Create() { struct Foo* foo = malloc(sizeof(Foo)); foo->p = malloc(sizeof(FooPrivate)); foo->x = 0; foo->y = 0; foo->p->z = 0; return foo; } void Foo_DoWhatever(struct Foo* foo) { foo->p->z = 4; /* Can access "private" parts of foo */ } 

在一个程序中:

  #include "foo.h" int main() { struct Foo* foo = Foo_Create(); foo->x = 100; /* Can access "public" parts of foo */ foo->p->z = 20; /* Error! FooPrivate is not fully declared here! */ Foo_DoWhatever(foo); /* Can call "member" function */ return 0; } 

请注意,需要使用“构造函数”函数来为私有数据分配内存。 显然,您需要将其与特殊的“析构函数”函数配对,以便正确释放私有数据。

或者,或者,如果您希望您的结构没有任何公共字段,您可以使整个结构不透明,并且只需要标题类似

  struct Foo; extern struct Foo* Foo_Create(); /* "constructor" */ extern void Foo_DoWhatever(struct Foo* foo); /* "member function" */ 

使用foo.c中struct Foo的实际定义,以及getter和setter函数可用于您希望直接访问的任何属性。

有时在C中使用的概念是

 // lib.h typedef struct { int publicInt; //... char * publicStr; } Public; Public * getPublic(); int function(Public * public); // lib.c typedef struct { Public public; int privateInt; // ... char * privateStr } Private; static Private * getPrivate(); Public * getPublic() { return (Public*) getPrivate(); } int function(Public * public) { Private * private = (Private *) public; // ... } 

这使用标准技巧,指向结构的指针可以与指向结构中第一个元素的指针互换。

如果您希望所有字段都是私有的,则更容易:

 // lib2.h typedef struct AllPrivate * Handle; Handle getHandle(); int function2(Handle handle); // lib2.c struct AllPrivate { /* ... */ } 

#include lib2.h的文件不会抱怨,因为我们只使用struct AllPrivate * ,并且所有指针都是相同的大小,因此编译器不需要知道struct AllPrivate的内部struct AllPrivate

要做一个受保护的区域,你只需要定义

 // include/public.h struct Public { /* ... */ } struct Public * getPublic(); int somePublicFunction(struct Public *); // dev/include/protected.h struct Protected { struct Public public; /* ... */ } struct Protected * getProtected(); int someProtectedFunction(struct Protected *); // dev/src/private.c struct Private { struct Protected protected; /* ... * /} struct Public * getPublic() { return (struct Public *) getPrivate(); } struct Public * getProtected() { return (struct Protected *) getPrivate(); } int somePublicFunction(struct Public * public) { struct Private private = (struct Private *) public; // ... } int someProtectedFunction(struct Protected * protected) { struct Private private = (struct Private *) protected; // ... } 

然后,这只是确保dev/include不被传递的问题。

对于数据字段 – 只是不要使用它们。 你可以做一些技巧,比如给他们疯狂的名字,以阻止他们的使用,但这不会阻止人们。 唯一真正的方法是创建另一个由库函数通过void指针访问的私有结构。

对于私有函数 – 使用文件static函数。 将所有库函数放在一个C文件中,并声明要将其作为static私有,并且不要将它们放在任何头文件中。

通常按惯例,私有成员的名称中有一个额外的下划线,或者附加了_pri类的_pri 。 或者可能是评论。 此技术不执行编译器强制检查以确保没有人不恰当地访问这些字段,但是作为对任何读取struct声明的人的警告,内容是实现细节,他们不应该查看或戳戳它们。

另一种常见技术是将结构暴露为不完整类型。 例如,在头文件中,您可能具有:

 struct my_struct; void some_function(struct my_struct *); 

在实现中,或者库的消费者无法访问的某些内部标题,您有:

 struct my_struct { /* Members of that struct */ }; 

您也可以使用void指针执行类似的技巧,这些技巧会被转换到代码的“私有”部分中的正确位置。 这种方法失去了一些灵活性(例如,您不能拥有未定义类型的堆栈分配实例),但这可能是可以接受的。

如果您希望混合使用私有成员和公共成员,则可以执行与上面相同的操作,但将私有结构指针存储为公共成员的成员,并使其在库的公共使用者中保持不完整。

虽然,这引入了一些间接性,这可能会损害性能。 有一些(通常是非便携式的,但可以在合理的编译器上工作)类型 – 惩罚技巧你也可以使用:

 struct public_struct { int public_member; int public_member2; /* etc.. */ }; struct private_struct { struct public_struct base_members; int private_member1; int private_member2; }; void some_function(struct public_struct *obj) { /* Hack alert! */ struct private_struct *private = (struct private_struct*)obj; } 

这也假定您不能将这些对象存储在堆栈或静态存储中,或者在编译时获取大小。

我为你感到难过,因为将不同的OO概念混合到C中通常就像一个圆孔中的方形钉。 话虽这么说, GObject支持公共和私人成员,但它是我在地球上最不喜欢的架构之一。 如果你不关心轻微的性能损失,你可以做一个更简单的解决方案 – 有一个充满私有成员的辅助结构,并从主(公共)结构有一个匿名指向该结构的指针。