【问题标题】:Emulate access specifiers in C在 C 中模拟访问说明符
【发布时间】:2012-06-13 03:05:58
【问题描述】:

是否可以在 C 中模拟 C++ 访问说明符 [public、private、protected]?更一般地说,C++ 编译器如何确保类的私有成员不被非成员函数访问?

【问题讨论】:

  • 重点是什么?这些关键字用于指定面向对象环境中的方法行为。
  • C++ 编译器读取代码并保留在何处可见的列表。在 C 中对此进行仿真需要使用宏和预编译器进行一些工作,并考虑在 C 程序中私有化的实际含义。而且只能粗略估算。
  • @Michael:好奇。由于可以使用 func 指针和 vtables 在 C 中实现虚函数机制,从而模拟多态性,我想知道是否可以模拟封装。
  • 在 C++ 中,访问说明符是类型系统的一部分。您必须在 C 中模拟 C++ 类型系统的这一方面。您将使用什么组合的预处理器和链接器魔法来完成这取决于您。在这里调用不透明类型是恕我直言。
  • 封装的问题在于它并不是真正的强制执行,而只是一种阻止成员访问并使访问变得更加晦涩的方法。如果有人真的想这样做,它将能够违反 C++ 封装。正如人们所说的 Python 缺乏封装强制一样,“我们都是在这里长大的”。如果您将属性设计为私有,那么直接使用它的人必须知道他/她在做什么,并承担风险。

标签: c++ c oop


【解决方案1】:

C++ 访问控制完全是编译器的臆想:你不能访问私有成员,因为编译器会拒绝编译任何试图这样做的代码。

通过诱使编译器认为指向ClassWithPrivateMember 实例的指针实际上是指向ClassWithPublicMember 实例的指针来访问C++ 类的私有成员实际上相当简单——即,通过使用稍加修改的头文件,您通常可以访问您不应该访问的内容。并不是说有人做过那样的事……

在 C 中进行访问控制的最佳方法是传递指向不透明类型的指针:struct 对象,其定义对客户端代码不可用。如果你提供一个foo* create_foo() 方法和一系列对foo* 进行操作的方法,对客户端隐藏foo 的实际定义,那么你将达到类似的效果。

// File "foo_private.h"
struct foo {
    int private1;
    char private2;
};

// File "foo.h"
typedef struct foo foo;
foo * create_foo(int x, char y);
int mangle_foo(foo *);

// file "foo.c"
#include <stdlib.h>
#include "foo.h"
#include "foo_private.h"

foo * create_foo(int x, char y) {
    foo * f = (foo *) calloc(1, sizeof(foo));
    f->private1 = x;
    f->private2 = y;
}    

int mangle_foo(foo *f) {
    return f->private1 + f->private2;
}

现在,您将编译到库中的foo.cfoo.h 一起分发。 foo.h 中声明的函数形成了一个类型的公共接口,但该类型的内部结构是不透明的;实际上,调用create_foo() 的客户端无法访问foo 对象的私有成员。

我们的朋友FILE* 是类似的东西,除了FILE 类型通常不是真正不透明的。只是大多数人(明智地)不会戳穿它的内脏。在那里,访问控制只是通过默默无闻来实施的。

【讨论】:

  • 感谢您的回答。能否举个例子,“一系列对void*进行操作的方法,对客户端隐藏实现,就可以达到类似的效果。”
  • 看,这正是 opaque 类型的用途,因为 void * 没有提供关于正在发生的事情的语义线索: typedef struct thing thing_t;并且您有一个类型,上面写着“内部没有用户可维修的部件”,同时仍然提供有关该类型应该做什么的信息。
  • void* 抛弃了类型安全。有更好的方法来做到这一点。
  • @blueshift -- 修改了我的示例以使用不透明类型。
  • 似乎仍然有点困惑,typedef struct f foo;struct foo;(未使用?),并且 create_foo 仍然返回 void。你测试过这个吗..?
【解决方案2】:

我强烈建议不要使用另一个答案中建议的 void* 指针(因为已修复),这会丢弃所有类型安全。您可以改为在标头中转发声明struct foo; 而无需指定内容,然后您可以将这些结构和指向它们的指针传入和传出标头中声明的接口函数。 struct 实现隐藏在该单元的 .c 文件中。

如果您想保留在结构和其他类型之间进行更改的选项,例如int,你可以在 header 中使用typedef 来包装接口的类型。

您可以使用的其他技术包括在该 .c 文件 static 中声明函数,以便它们无法从其他来源链接,即使这些其他来源声明了该函数。

【讨论】:

    【解决方案3】:

    有很多方法可以实现目标,以下是我的:

    示例包括一个类“struct test_t”和一个类函数“test_create”和一个成员函数“print”

    test.h:

    struct test_t {
        // Member functions
        void (*print)(struct test_t *thiz);
    
        // Private attributes
        char priv[0];
    };
    
    
    // Class functions
    struct test_t *test_create(int number);
    

    test.c:

    #include "test.h"
    #include <stdio.h>
    #include <stdlib.h>
    
    // priv attr
    struct test_priv_t {
        int number;
    };
    
    
    // member functions
    static void print(struct test_t *thiz)
    {
        struct test_priv_t *priv = (struct test_priv_t*)thiz->priv;
        printf("number = %d\n", priv->number);
    }
    
    
    // Class functions
    struct test_t *test_create(int number)
    {
        struct test_t *test = (struct test_t *)malloc(sizeof(struct test_t) + sizeof(struct test_priv_t));
    
        // setup member function
        test->print = print;
    
        // initialize some priv attr
        struct test_priv_t *priv = (struct test_priv_t*)test->priv;
        priv->number = number;
    
        return test;
    }
    

    main.c:

    #include "test.h"
    
    int main()
    {
        struct test_t *test = test_create(10);
        test->print(test);
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-09-17
      • 2018-01-30
      • 2012-06-05
      • 2011-04-16
      • 1970-01-01
      • 2016-06-16
      • 2011-01-31
      • 1970-01-01
      相关资源
      最近更新 更多