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

发布于 2024-09-25 14:28:07 字数 290 浏览 7 评论 0原文

在 C++ 和 Java 中,数据结构可以具有私有公共受保护区域。我想将这个概念移植到我正在编写的 C 语言程序中。

在 C 结构体中是否有实现私有或受保护函数指针和数据字段的习惯用法? 我知道 C struct 是公共的,我正在寻找一种习惯用法来帮助隐藏一些实现细节并强制用户使用公共接口。

注意:该语言已由商店选择,因此我一直在将面向对象的概念实施到 C 中。

谢谢。

In C++ and Java, data structures can have private, public and protected regions. I'd like to port this concept to a C language program I am writing.

Are there any idioms for implementing private or protected function pointers and data fields in a C struct?
I know that C structs are public, I'm looking for an idiom to help hide some implementation details and force users to use the public interface.

Note: The language has been chosen by the shop, so I am stuck implementing Object Oriented concepts into C.

Thanks.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

[旋木] 2024-10-02 14:28:07

如您所知,您不能这样做。然而,有一些习语可以达到类似的效果。

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" */

struct Foo的实际定义 在 foo.c 中,并且 getter 和 setter 函数可用于您想要提供直接访问的任何属性。

As you know, you cannot do this. However, there are idioms that will allow a similar effect.

C will allow you do do something similar to what is known as the "pimpl" idiom in object-oriented design. Your struct can have an opaque pointer to another forward-declared struct that acts as the struct's private data. Functions that operate on the struct, taking the place of member functions, can have the full definition for the private member, and can make use of it, while other parts of the code cannot. For example:

In a header, 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" */

In the implementation, 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 */
  }

In a program:

  #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;
  }

Note the need to use a "constructor" function in order to allocate memory for the private data. Obviously you would need to pair this with a special "destructor" function in order to deallocate the private data properly.

Or, alternatively, if you would like your struct to have no public fields whatsoever, you could make the entire struct opaque, and just have the header be something like

  struct Foo;

  extern struct Foo* Foo_Create(); /* "constructor" */

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

With the actual definition of struct Foo in foo.c, and getter and setter functions available for any properties you would like to provide direct access to.

べ映画 2024-10-02 14:28:07

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的内部结构。

要创建受保护区域,您只需定义

// 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 不被传递即可。

The concept sometimes used in C is

// 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;
  // ...
}

This uses the standard trick that a pointer to a struct can be interchanged with a pointer to the first element in a struct.

If you want all your fields to be private, it's even easier:

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

// lib2.c
struct AllPrivate { /* ... */ }

Files that #include lib2.h won't complain, since we only use struct AllPrivate *, and all pointers are the same size, so the compiler doesn't need to know the innards of struct AllPrivate.

To do a protected region, you'd just have to define

// 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;
  // ...
}

Then it's just a matter of making sure that dev/include isn't passed around.

鱼忆七猫命九 2024-10-02 14:28:07

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

对于私有函数——使用文件静态函数。将所有库函数放入一个 C 文件中,并将要设为私有的库函数声明为 static,并且不要将它们放入任何头文件中。

For data fields -- just don't use them. You can do some tricks like giving them crazy names to discourage their use, but that won't stop people. The only real way to do it is to make another private struct that is accessed by a void pointer by your library functions.

For private functions -- use file static functions. Put all of your library functions in one C file and declare the ones that you want to be private as static and don't put them in any header files.

清泪尽 2024-10-02 14:28:07

通常按照约定,私有成员的名称中会有一个额外的下划线,或者附加类似 _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;
}

这还假设您无法将这些对象存储在堆栈或静态存储中,或者无法在编译时获取大小时间。

Often by convention, a private member has an extra underscore in its name, or something like _pri appended. Or possibly a comment. This technique doesn't do compiler-enforced checking to make sure no one inappropriately accesses those fields, but serves as a warning to anyone reading a struct declaration that the contents are implementation details and they shouldn't peek or poke at them.

Another common technique is to expose your structure as an incomplete type. For example, in your header file, you might have:

struct my_struct;

void some_function(struct my_struct *);

And in the implementation, or some internal header which is not accessible to consumers of the library, you have:

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

You can also do similar tricks with void pointers, that get cast to the right place in the "private" portion of the code. This approach loses some flexibility (you can't have a stack-allocated instance of an undefined type, for example), but that may be acceptable.

If you want to have a mixture of private and public members, you can do the same thing as above, but store the private struct pointer as a member of the public one, and leave it incomplete in public consumers of the library.

Although, this introduces some indirection, which may hurt performance. There are some (generally non-portable, but will work on reasonable compilers) type-punning tricks you can use too:

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;
}

This also assumes that you can't store these objects on the stack or in static storage, or get the size at compile time.

行雁书 2024-10-02 14:28:07

我为你感到遗憾,因为将不同的 OO 概念混入 C 中通常就像圆孔中的方钉一样。话虽这么说,GObject 支持公共和私有成员,但它是我最不喜欢的体系结构之一。如果您不关心较小的性能影响,您也许可以采取更简单的解决方案 - 有一个充满私有成员的辅助结构,并有一个从主(公共)结构指向该结构的匿名指针。

I feel sorry for you, because mashing different OO concepts into C is often like a square peg in a round hole. That being said, GObject has support for public and private members, but it's one of my least favourite architectures on earth. If you're not concerned with the minor performance hit, you may be able to do a simpler solution - have a secondary struct that's filled with private members, and have an anonymous pointer to that struct from the primary (public) struct.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文