您可以使用常规函数和虚拟表(vtable)实现多态性.这是一个非常简洁的系统,我发明了(基于C++)进行编程练习:
构造函数分配内存,然后调用类初始化内存的init函数.每个init函数还应包含一个静态vtable结构,其中包含虚函数指针(纯虚拟的NULL).派生类init函数在执行任何其他操作之前调用超类init函数.
通过实现虚函数包装器(不要与vtables指向的函数混淆)可以创建一个非常好的API,如下所示(static inline
如果你在标题中添加它,则在它前面添加):
int playerGuess(Player* this) { return this->vtable->guess(this); }
单继承可以通过滥用结构的二进制布局来完成:
请注意,多重继承更加混乱,因为在层次结构类型之间进行转换时,通常需要调整指针值.
其他类型特定的数据也可以添加到虚拟表中.示例包括运行时类型信息(例如,类型名称作为字符串),链接到超类vtable和析构函数链.您可能需要虚拟析构函数,其中派生类析构函数将对象降级为其超类,然后递归调用它的析构函数,依此类推,直到到达基类析构函数并最终释放结构.
封装是通过在player_protected.h中定义结构并在player_protected.c中实现函数(由vtable指向)来完成的,对于派生类也是如此,但这非常笨拙并且会降低性能(因为虚拟包装器不能放入标题),所以我建议反对它.
您可以使用常规函数和虚拟表(vtable)实现多态性.这是一个非常简洁的系统,我发明了(基于C++)进行编程练习:
构造函数分配内存,然后调用类初始化内存的init函数.每个init函数还应包含一个静态vtable结构,其中包含虚函数指针(纯虚拟的NULL).派生类init函数在执行任何其他操作之前调用超类init函数.
通过实现虚函数包装器(不要与vtables指向的函数混淆)可以创建一个非常好的API,如下所示(static inline
如果你在标题中添加它,则在它前面添加):
int playerGuess(Player* this) { return this->vtable->guess(this); }
单继承可以通过滥用结构的二进制布局来完成:
请注意,多重继承更加混乱,因为在层次结构类型之间进行转换时,通常需要调整指针值.
其他类型特定的数据也可以添加到虚拟表中.示例包括运行时类型信息(例如,类型名称作为字符串),链接到超类vtable和析构函数链.您可能需要虚拟析构函数,其中派生类析构函数将对象降级为其超类,然后递归调用它的析构函数,依此类推,直到到达基类析构函数并最终释放结构.
封装是通过在player_protected.h中定义结构并在player_protected.c中实现函数(由vtable指向)来完成的,对于派生类也是如此,但这非常笨拙并且会降低性能(因为虚拟包装器不能放入标题),所以我建议反对它.
你读过关于这个主题的"圣经"吗?请参阅面向对象的C ...
你会如何模仿封装和继承?
实际上,封装是最容易的部分.封装是一种设计理念,它与语言无关,也与你如何思考问题无关.
例如,Windows FILE api是完全封装的.当您打开文件时,您将获得一个不透明对象,其中包含文件"对象"的所有状态信息.您将此句柄交还给每个文件io apis.封装实际上是远优于C++,因为没有公共的头文件,人们可以看看,看看你的私有变量的名称.
继承更难,但为了使代码面向对象,它根本不是必需的.在某些方面,聚合比继承更好,并且聚合在C中和在C++中一样容易.看到这个的实例.
作为对Neil的回应,请参阅维基百科,了解为什么继承不是多态性所必需的.
在C++编译器可用之前的几年,我们的老人都编写了面向对象的代码,这是一个思维模式而不是工具集.
Apple的基于C语言的CoreFoundation框架实际上是为了使其"对象"能够作为Objective-C(一种实际的OO语言)中的对象而加倍.该框架的一个相当大的子集是Apple网站上的开源CF-Lite.可能是以这种方式完成的主要操作系统级框架中的有用案例研究.
从高度稍微高一点开始考虑问题而不是像OOP主流所暗示的那样更开放,面向对象编程意味着将对象视为具有相关功能的数据.它并不一定意味着函数必须物理地附加到对象,因为它是支持OOP范例的流行语言,例如在C++中:
struct T { int data; int get_data() const { return data; } };
我建议仔细看看GTK +对象和类型系统.这是用C编程语言实现的OOP的一个很好的例子:
GTK +实现了自己的自定义对象系统,该系统提供标准的面向对象功能,如继承和虚函数
该协会也可以是合同和传统的.
关于封装和数据隐藏技术,流行和简单的可能是不透明指针(或不透明数据类型) - 你可以传递它,但为了加载或存储任何信息,你必须调用相关的功能,知道如何与隐藏在那个不透明指针后面的对象.
另一个,类似但不同的是Shadow Data类型 - 检查这个链接,Jon Jagger对这个不太知名的技术给出了很好的解释.