代码之家  ›  专栏  ›  技术社区  ›  Thomas Matthews

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

  •  14
  • Thomas Matthews  · 技术社区  · 15 年前

    private , public protected 区域。我想把这个概念移植到我正在编写的一个C语言程序中。

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

    谢谢。

    5 回复  |  直到 15 年前
        1
  •  28
  •   Tyler McHenry    15 年前

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

    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 opaque,只需使头类似于

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

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

        2
  •  9
  •   rampion    15 年前

    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 没有被传递。

        3
  •  2
  •   nategoose    15 年前

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

    对于私有函数——使用文件 static 功能。将所有库函数放在一个C文件中,并将要私有的函数声明为 静止的 别把它们放在任何头文件里。

        4
  •  1
  •   asveikau    15 年前

    通常按照惯例,私有成员的名称中有一个额外的下划线,或者类似的 _pri struct 声明内容是实现细节,他们不应该偷看或戳他们。

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

    struct my_struct;
    
    void some_function(struct my_struct *);
    

    在实现中,或库的使用者无法访问的某些内部标头中,您有:

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

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

    不过,这会引入一些间接性,这可能会影响性能。您也可以使用一些类型双关技巧(通常不可移植,但适用于合理的编译器):

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

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

        5
  •  0
  •   Reinderien    15 年前

    GObject 支持公共和私人成员,但它是我最不喜欢的建筑之一。如果您不关心性能的轻微影响,您可以做一个更简单的解决方案-有一个填充了私有成员的辅助结构,并从主(公共)结构中有一个指向该结构的匿名指针。