C语言中的面向对象编程

Chi*_*chi 54 c oop

可能重复:
你能用C编写面向对象的代码吗?
C中的面向对象模式?

我记得刚才读到有人(我认为是Linus Torvalds)谈论C++是一种可怕的语言,以及如何用C编写面向对象的程序.在有时间反思的时候,我真的不知道如何所有面向对象的概念都延续到了C.有些事情是相当明显的.例如:

  1. 要模拟成员函数,可以将函数指针放在结构中.
  2. 要模拟多态性,你可以编写一个函数,它接受可变数量的参数并根据sizeof参数(例如参数)执行一些voodoo

你会如何模仿封装和继承?

我想通过使用存储私有成员的嵌套结构可以模拟封装.它可以相当容易地出现,但也许可以被命名PRIVATE或者同样明显的东西,以表明它不应该从结构外部使用.那继承怎么样?

Tro*_*nic 76

您可以使用常规函数和虚拟表(vtable)实现多态性.这是一个非常简洁的系统,我发明了(基于C++)进行编程练习:替代文字

构造函数分配内存,然后调用类初始化内存的init函数.每个init函数还应包含一个静态vtable结构,其中包含虚函数指针(纯虚拟的NULL).派生类init函数在执行任何其他操作之前调用超类init函数.

通过实现虚函数包装器(不要与vtables指向的函数混淆)可以创建一个非常好的API,如下所示(static inline如果你在标题中添加它,则在它前面添加):

int playerGuess(Player* this) { return this->vtable->guess(this); }
Run Code Online (Sandbox Code Playgroud)

单继承可以通过滥用结构的二进制布局来完成: 替代文字

请注意,多重继承更加混乱,因为在层次结构类型之间进行转换时,通常需要调整指针值.

其他类型特定的数据也可以添加到虚拟表中.示例包括运行时类型信息(例如,类型名称作为字符串),链接到超类vtable和析构函数链.您可能需要虚拟析构函数,其中派生类析构函数将对象降级为其超类,然后递归调用它的析构函数,依此类推,直到到达基类析构函数并最终释放结构.

封装是通过在player_protected.h中定义结构并在player_protected.c中实现函数(由vtable指向)来完成的,对于派生类也是如此,但这非常笨拙并且会降低性能(因为虚拟包装器不能放入标题),所以我建议反对它.

  • +1漂亮的照片. (7认同)
  • C++也实现了这样的OOP. (4认同)

Kor*_*icz 29

你读过关于这个主题的"圣经"吗?请参阅面向对象的C ...


Joh*_*ler 8

你会如何模仿封装和继承?

实际上,封装是最​​容易的部分.封装是一种设计理念,它与语言无关,也与你如何思考问题无关.

例如,Windows FILE api是完全封装的.当您打开文件时,您将获得一个不透明对象,其中包含文件"对象"的所有状态信息.您将此句柄交还给每个文件io apis.封装实际上是优于C++,因为没有公共的头文件,人们可以看看,看看你的私有变量的名称.

继承更难,但为了使代码面向对象,它根本不是必需的.在某些方面,聚合比继承更好,并且聚合在C中和在C++中一样容易.看到这个的实例.

作为对Neil的回应,请参阅维基百科,了解为什么继承不是多态性所必需的.

在C++编译器可用之前的几年,我们的老人都编写了面向对象的代码,这是一个思维模式而不是工具集.

  • @Neil:我不认为你经常犯错误,但在这方面你完全错了.继承不是OO的必要特征.您可以通过聚合/委派获得多态性. (2认同)

Chu*_*uck 5

Apple的基于C语言的CoreFoundation框架实际上是为了使其"对象"能够作为Objective-C(一种实际的OO语言)中的对象而加倍.该框架的一个相当大的子集是Apple网站上的开源CF-Lite.可能是以这种方式完成的主要操作系统级框架中的有用案例研究.


mlo*_*kot 5

从高度稍微高一点开始考虑问题而不是像OOP主流所暗示的那样更开放,面向对象编程意味着将对象视为具有相关功能的数据.它并不一定意味着函数必须物理地附加到对象,因为它是支持OOP范例的流行语言,例如在C++中:

struct T
{
   int data;
   int get_data() const { return data; }
};
Run Code Online (Sandbox Code Playgroud)

我建议仔细看看GTK +对象和类型系统.这是用C编程语言实现的OOP的一个很好的例子:

GTK +实现了自己的自定义对象系统,该系统提供标准的面向对象功能,如继承和虚函数

该协会也可以是合同和传统的.

关于封装和数据隐藏技术,流行和简单的可能是不透明指针(或不透明数据类型) - 你可以传递它,但为了加载或存储任何信息,你必须调用相关的功能,知道如何与隐藏在那个不透明指针后面的对象.

另一个,类似但不同的是Shadow Data类型 - 检查这个链接,Jon Jagger对这个不太知名的技术给出了很好的解释.