在明确概念之前,多态性和继承的C语言习惯用法是什么?

在明确概念之前,多态性和继承的C语言习惯用法是什么?,c,history,C,History,假设我有一个普通的类Animal抽象类和类Dog:public Animal类Cat:public Animal,这使它成为一个可以实例化对象的具体类。进一步假设您有一个函数foo(动物a),将猫或狗作为对象。在早期,C++将编译成C,并构建一个 VTABs/COD>维护对象。 但我的一个学生问我这样一个问题:在程序员们通常谈论这些概念之前,他们在日常的C语言编码中是如何做到的?用C编写这些概念的惯用方法是什么 我已经对linux内核和其他OSS项目进行了筛选,但是我没有找到一个清晰的模式:有时

假设我有一个普通的
类Animal
抽象类和
类Dog:public Animal
类Cat:public Animal
,这使它成为一个可以实例化对象的具体类。进一步假设您有一个函数
foo(动物a)
,将猫或狗作为对象。在早期,C++将编译成C,并构建一个<代码> VTABs/COD>维护对象。 但我的一个学生问我这样一个问题:在程序员们通常谈论这些概念之前,他们在日常的C语言编码中是如何做到的?用C编写这些概念的惯用方法是什么

我已经对linux内核和其他OSS项目进行了筛选,但是我没有找到一个清晰的模式:有时是联合(针对不同的结构),有时是函数指针,等等。但是我希望业界有知识的人能给出一个直接的答案,他们已经做过并且有很多C方面的经验


用一句话来说:什么是用于继承和多态性的惯用C

简单的程序,例如为学校作业编写的程序,使用由一个联合和可选的枚举组成的结构作为类型鉴别器来实现多态性。然后,每个“方法”都包含一个switch语句,该语句调用适合该子类型的函数。显然,这不能扩展到更多需要在不改变基类定义的情况下添加子类的系统

多态性本身很容易用接收显式
self
参数的函数指针表示。通过嵌入其超类的“继承”结构可以实现开放式继承:

struct base {
   // ... members here ...
};

struct inherited {
  struct base base;
  // ... inherited members here ...
};
指向继承的
struct
的指针可以安全地强制转换到
struct base*
,这是C标准明确支持的做法。这些强制转换通常隐藏在宏后面,宏甚至可能执行运行时类型检查(如果可能)

实现这一点相当麻烦,因为没有模板,没有析构函数的自动调用,没有异常,也没有STL。换句话说,程序员必须小心处理错误处理和析构函数调用,类型差异必须在运行时通过回调(考虑
std::sort()
qsort()
之间的差异)或通过难以维护的预处理器技巧来处理


尽管存在困难,但在C中实现C++的功能性的一个有意义的子集,甚至在过程中实现C的简单性是可能的。为了研究将这种方法应用到生产级的实际示例,请查看CPython解释器的实现,或GTK+使用的glib对象系统。

在面向对象编程的发展之前,应用程序使用的是过程代码,其中功能是“封装”的在功能的层次结构中,位于层次结构顶部的功能处理抽象,并且随着层次结构的深入而变得越来越具体和详细。一些API提供了创建只能通过句柄操作的结构的方法。示例:在C中,文件句柄隐藏处理文件的详细信息


至于在C中实现OO,我相信如果没有适当的语言支持和适当的结构,它永远都不是一个可行的选择。用指向函数的指针伪造OO所带来的痛苦是不值得的。

我看到了使用
#defines
在源代码中创建抽象名称并包含文件的代码。例如,相同的例程可能被重新用于处理字节、
short
s、
int
s、
float
s和
double
s。代码凌乱且难以理解。

1)智能联合2)作为结构/联合成员的函数指针。举个例子,看看Linux如何处理(块)设备驱动程序。我不认为有一个习惯性的方式来做它,这是一个卖点的C++。我还想到了这篇文章(关于在C中模拟OO)@wildplasser:谢谢。“聪明工会”与“工会”不同吗?我不熟悉这个概念,因为有“指针”,然后才有“智能指针”。智能联合是具有类似结构的联合,第一部分对所有成员都是相同的,并且(在大多数情况下)它包含一个
类型
字段。@wildplasser:谢谢。如果你能用一个例子写一个简短的答案就好了。。。为了存档:)谢谢!也许正是您的这段回答澄清了很多:“指向继承的struct的指针可以安全地转换到struct base,这是一种受到C标准明确支持的做法。”这很有帮助。我认为这是他们做的方式,所以标准会明确地说。@ DrVithHuxk是的,这就是C++所称的“Up石膏”,甚至是隐式执行。相反的是:“向下流延”,比如说,当你在代码中通过<代码> StuttBase< /C> >,并且需要找到“真实”对象时。我猜您也可以在函数中传递对象的类型,或者该类型是结构中的某个枚举?这是常见的做法吗?@DervinThunk向下转换的执行方式与C++中完全相同:将
struct base*
转换为
struct inherited*
。要执行与错误检查动态强制转换等效的操作,需要一个类型鉴别器。在更简单的系统中,这是一个简单的枚举,而在更复杂的系统中,这是一个指向描述类型的详细结构的指针,类似于C++的vtable指针。@DervinThunk他们不仅在过去这样做,现在仍然这样做-有一种东西叫做现代C代码。:)许多Unix程序和服务都是用C编写的,对Python、Perl或Ruby等流行解释语言的典型扩展也是用C编写的。这样的C代码大量使用上述面向对象技术。举个例子