【问题标题】:Implementing data hiding access specifiers in C language用 C 语言实现数据隐藏访问说明符
【发布时间】:2016-07-12 11:16:04
【问题描述】:

有没有一种方法可以在 C 语言中实现访问说明符,例如“private”、“protected”。我在互联网上遇到了关于使用“静态”和“ifdefs”使函数仅在某些其他函数中可用的解决方案。

除此之外,是否有任何 C 实现等效于在 C++ 类中使用私有和受保护的访问说明符?

【问题讨论】:

  • C 已经完成,所以你应该可以。问题是是否值得付出努力,代码是否仍可维护。如果您想要 C++,请使用 C++,而不是 C。它们是不同的语言。 (见鬼,为什么没有人要求在 SQL 中使用 inline-Assembler?)
  • @Olaf 在 C 中进行 OOP 是完全合理的,对于任何复杂的 C 项目,我认为这是最好的方法。缺点是需要更多样板代码,并且编译器无法检测到 C++ 编译器会检测到或根本不允许等效代码的部分错误。 C++ 风格的 RAII 也不可用。好处是,程序员可以完全控制代码中发生的事情,没有隐式代码执行(没有异常或隐式构造函数和析构函数),并且 vtable 也由程序员直接控制。
  • @Olaf 图灵完整性与含糖语言功能无关。
  • @hyde:我反对尝试用大锤在 C 中实现 C++ 特性。并且使 C 代码膨胀到可维护性之外(顺便说一句,这也适用于宏)。如果您查看我的历史,您可能会注意到我非常支持 C 反对 C++在合理的情况下。 OP 显然超越了这一点(不是我从来没有这样做过;这是一个永久的诱惑)。
  • @Olaf 访问说明符在定义上是糖——它们是不允许程序做以前不能做的任何事情的语言结构。

标签: c oop


【解决方案1】:

C 没有访问说明符。向调用者隐藏某些内容的唯一方法是不在标题中提供其声明。

您可以在翻译单元中将其设为静态:

myapi.h

extern int visibleVariable;
void visibleFunction();

myapi.c

int visibleVariable;
static int invisibleVariable;
void visibleFunction() {
    ...
}
static void invisibleFunction() {
    ...
}

您还可以通过将struct 的定义放在实现文件中来隐藏它。这样,您的结构的所有字段都将对翻译单元私有。这种方法的缺点是您的 API 的用户将无法声明您的 struct 类型的变量,因此他们需要通过指针处理您的 struct

C 没有继承的概念,因此不存在等同于protected 的访问权限。

【讨论】:

  • 嗯,int i; 在实现中也是静态的。你的意思是static。但似乎没有一个是 OP 真正想要的。
  • @Olaf 这似乎是 OP 想要的一部分,他只是想要更多。
  • 他似乎想要一个带有部分不可见成员的struct。很有趣,因为 C++ 并没有真正隐藏它们,只是让它们更难触及。
  • @hyde 我重新构造了句子,使它不再暗示static 是唯一的出路。谢谢!
【解决方案2】:

C 没有用户可定义的名称空间或访问说明符。由于您排除(ab)使用预处理器,因此尝试访问“类”的私有部分时出现编译器错误的唯一方法是没有一个暴露“私有”内容的 .h 文件。它们仍然可以放入“私有”单独的 .h 文件(包含在模块或库自己的 .c 文件中,但不意味着包含在应用程序代码中),或隐藏在 #ifdefs 后面(需要特殊定义才能激活“私人”部分)。

隐藏事物的一种常见方法是使用不透明结构,也称为不透明指针。对于这种方法,模块或库之外的代码只有指向结构的指针,但没有结构定义。然后它使用模块提供的函数来获取一个实例,访问它,最后释放它。

通过这种方法,您可以轻松获得公共接口:您在公共 .h 文件中提供的功能,以及任何在那里定义的公共支持结构。私有接口是完整结构定义可见的代码,以及不在公共 .h 文件中的任何函数。

受保护的访问意味着继承,当使用 C 手动实现时,它的工作方式通常与 C++ 非常不同,并且这个主题过于广泛,无法在此答案中涵盖。最接近这种情况的可能是有几个 .h 文件,它们提供了几个级别的“公共”访问,然后程序员有责任不与它们发生问题。

这种方法的好处是,如果结构发生变化,则不需要修改(甚至重新编译)使用该模块的其他代码。通常 struct 甚至可能是一个联合,然后模块的函数会根据实际类型进行分支,所有这些都不会从使用它的代码中看到。另一个好处是,该模块可以控制结构的创建,因此它可以拥有一个结构池并避免使用堆,所有这些对应用程序代码都是不可见的。一个缺点是,你不能有内联函数(因为 .h 文件中的内联函数体需要结构定义,我们试图在这里隐藏它),这会在性能受到关注的情况下阻止一些很好的编译器优化。

示例(为此答案编写的未经测试的代码):

模块.h:

// ...other standard header file stuff ...

// forward declaration of struct
struct module_data;

// "constructor" function
struct module_data *module_initialize_data(int value);

// modification function
int module_update_data(struct module_data *data, int adjust);

// "destructor" function
void module_release(struct module_data *data);

模块.c

#include "module.h"

// struct definition only in the .c file
struct module_data {
   int value;
};

struct module_data *module_initialize_data(int value) {
    struct module_data *data = malloc(sizeof(*data));
    data->value = value;
    return data;
}

int module_update_data(struct module_data *data, int adjust) {
    data->value += adjust;
    return data->value;
}

void module_release(struct module_data *data) {
    free(data);
}

相关维基百科链接供参考:

【讨论】:

  • C 有命名空间!它只是没有用户定义的命名空间。您的方法不允许 OP 要求的内容。实现它会更复杂(和废话)。
  • @Olaf 你是说范围还是什么?
  • 我可以区分命名空间和范围。阅读标准。 (它还区分范围和名称空间)。
  • 添加了“用户可定义”以更准确(还有其他编辑)。
  • 这并没有改变我的其余评论。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-09-17
  • 2018-01-30
  • 1970-01-01
  • 1970-01-01
  • 2020-08-08
  • 2012-03-02
  • 1970-01-01
相关资源
最近更新 更多