我建议不要使用预处理器(ab)来尝试使 C 语法更像另一种更面向对象的语言。在最基本的层面上,您只需使用普通结构作为对象并通过指针传递它们:
struct monkey
{
float age;
bool is_male;
int happiness;
};
void monkey_dance(struct monkey *monkey)
{
/* do a little dance */
}
为了获得像继承和多态这样的东西,你必须付出更多的努力。您可以通过将结构的第一个成员作为超类的实例来进行手动继承,然后您可以自由地转换指向基类和派生类的指针:
struct base
{
/* base class members */
};
struct derived
{
struct base super;
/* derived class members */
};
struct derived d;
struct base *base_ptr = (struct base *)&d; // upcast
struct derived *derived_ptr = (struct derived *)base_ptr; // downcast
为了获得多态性(即虚拟函数),您可以使用函数指针和可选的函数指针表,也称为虚拟表或 vtable:
struct base;
struct base_vtable
{
void (*dance)(struct base *);
void (*jump)(struct base *, int how_high);
};
struct base
{
struct base_vtable *vtable;
/* base members */
};
void base_dance(struct base *b)
{
b->vtable->dance(b);
}
void base_jump(struct base *b, int how_high)
{
b->vtable->jump(b, how_high);
}
struct derived1
{
struct base super;
/* derived1 members */
};
void derived1_dance(struct derived1 *d)
{
/* implementation of derived1's dance function */
}
void derived1_jump(struct derived1 *d, int how_high)
{
/* implementation of derived 1's jump function */
}
/* global vtable for derived1 */
struct base_vtable derived1_vtable =
{
&derived1_dance, /* you might get a warning here about incompatible pointer types */
&derived1_jump /* you can ignore it, or perform a cast to get rid of it */
};
void derived1_init(struct derived1 *d)
{
d->super.vtable = &derived1_vtable;
/* init base members d->super.foo */
/* init derived1 members d->foo */
}
struct derived2
{
struct base super;
/* derived2 members */
};
void derived2_dance(struct derived2 *d)
{
/* implementation of derived2's dance function */
}
void derived2_jump(struct derived2 *d, int how_high)
{
/* implementation of derived2's jump function */
}
struct base_vtable derived2_vtable =
{
&derived2_dance,
&derived2_jump
};
void derived2_init(struct derived2 *d)
{
d->super.vtable = &derived2_vtable;
/* init base members d->super.foo */
/* init derived1 members d->foo */
}
int main(void)
{
/* OK! We're done with our declarations, now we can finally do some
polymorphism in C */
struct derived1 d1;
derived1_init(&d1);
struct derived2 d2;
derived2_init(&d2);
struct base *b1_ptr = (struct base *)&d1;
struct base *b2_ptr = (struct base *)&d2;
base_dance(b1_ptr); /* calls derived1_dance */
base_dance(b2_ptr); /* calls derived2_dance */
base_jump(b1_ptr, 42); /* calls derived1_jump */
base_jump(b2_ptr, 42); /* calls derived2_jump */
return 0;
}
这就是在 C 中实现多态性的方法。它并不漂亮,但它可以完成工作。有一些棘手的问题涉及基类和派生类之间的指针转换,只要基类是派生类的第一个成员,这些问题就是安全的。多重继承要困难得多 - 在这种情况下,为了在第一个基类之外的基类之间进行区分,您需要根据正确的偏移量手动调整指针,这确实很棘手且容易出错。
您可以做的另一件事(棘手)是在运行时更改对象的动态类型!您只需为它重新分配一个新的 vtable 指针即可。您甚至可以有选择地更改某些虚拟功能,同时保留其他虚拟功能,从而创建新的混合类型。只是要小心创建一个新的 vtable 而不是修改全局 vtable,否则你会意外地影响给定类型的所有对象。