您可以使用函数指针模拟虚函数。例如,
struct foo
{
void(*bar)(struct foo*, int, int);
};
void default_bar ( struct foo * f, int a, int b )
{
printf("bar(%d,%d)\n", a, b);
}
void setup_foo ( struct foo * f )
{
f->bar = &default_bar;
}
然后,您可以将结构“子类化”为:
struct meh
{
/* inherit from "class foo". MUST be first. */
struct foo base;
int more_data;
};
/* override "method bar". */
struct custom_bar ( struct foo * f, int a, int b )
{
struct meh * m = (struct meh*)f;
printf("custom_bar(%d,%d)\n", a, b);
}
void setup_meh ( struct meh * m )
{
setup_foo(&m->base);
m->bar = &custom_bar;
}
所有这些都是劳动密集型且容易出错的,但可以做到。这种类型的“继承”和“覆盖”实现在一些著名的 C 库中很常见,包括jpeglib 和libpng。如果您对标准 C I/O 不满意,他们使用此技术允许您覆盖 I/O 过程。
编辑:如 cmets 中所述,其中一些代码依赖(官方)非标准行为,“恰好”在大多数编译器上工作。主要问题是代码假定&m.base == &m(例如base 成员的偏移量为0)。如果不是这种情况,则custom_bar() 中的强制转换会导致未定义的行为。要解决此问题,您可以在 struct foo 中添加一个额外的指针,如下所示:
struct foo
{
/* same as before ...*/
/* extra pointer. */
void * hook;
};
然后,修改触及演员的东西,
void setup_meh ( struct meh * m )
{
m->base.hook = m;
/* set up function pointers as usual... */
}
void custom_bar ( struct foo * f, int a, int b )
{
struct meh * m = (struct meh*)f->hook;
/* override. */
}
这种技术更可靠,特别是如果您打算用 C++ 编写“派生结构”并使用虚函数。在这种情况下,第一个成员的偏移量通常为非 0,因为编译器会在其中存储运行时类型信息和类的 v-table。