在OP的例子中,struct Alien
延伸struct Thing
并添加新的“virtual“函数(在通过动态调度的函数的意义上vtables https://en.wikipedia.org/wiki/Virtual_method_table),或者换句话说AlienVTable
扩展基础ThingVTable
.
struct Thing { struct Alien {
---\ /---
/--- VTable *vtable; | | VTable *vtable; ----\
| char *name; | ---> | char *name; |
| ---/ \--- |
| }; /* other Alien members*/ |
| }; |
| |
|--> struct ThingVTable { struct AlienVTable { <---|
---\ /---
void (*print)(Thing *self); | ---> | void (*print)(Thing *self);
---/ \---
void (*function)(Alien *self);
/* other virtual Alien functions */
}; };
以下是实现此目的的一种方法(注释参考了某些构造的粗略 C++ 等效项,尽管 C 代码确实not完全复制 C++ 语义)。
#ifdef _MSC_VER
#define _CRT_NONSTDC_NO_DEPRECATE // msvc strdup c4996
#endif
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef struct Thing { // struct Thing {
const struct ThingVTable *vtable; //
char *name; // char *name;
} Thing; //
//
typedef struct ThingVTable { //
const void *base_vtable; //
void (*print_name)(Thing *self); // virtual void print_name();
void (*print_age)(Thing *self); // virtual void print_age() = 0;
} ThingVTable; // };
void print_thing_name(Thing *self) // void Thing::print_name()
{ printf("Thing name: %s\n", self->name); } // { ... }
static const ThingVTable thing_vtable = {
.base_vtable = NULL,
.print_name = print_thing_name,
.print_age = NULL
};
void construct_thing(Thing *self, const char *name) // Thing::Thing(const char *name)
{ // : name(name) { ... }
self->vtable = &thing_vtable; //
self->name = strdup(name); //
} //
//
void destruct_thing(Thing *self) // Thing::~Thing()
{ // { ... }
free(self->name);
self->vtable = NULL;
}
Thing *new_thing(const char *name) // Thing *p = new Thing(name);
{ //
Thing *self = malloc(sizeof(Thing)); //
if (self == NULL) return NULL; //
//
construct_thing(self, name); //
return self; //
} //
//
void delete_thing(Thing *self) // delete p;
{
destruct_thing(self);
free(self);
}
typedef struct Alien { // struct Alien : Thing {
Thing super; //
int age; // int age;
} Alien; //
//
typedef struct AlienVTable { // void print_name() override;
ThingVTable super; // void print_age() override;
int (*is_et)(struct Alien *); // virtual int is_et();
// };
} AlienVTable;
// override of base virtual function
void print_alien_name(Thing *self) // void print_name()
{ printf("Alien name: %s\n", self->name); } // { ... }
//
// implementation of base pure virtual function
void print_alien_age(Thing *self) // void print_age()
{ printf("Alien age: %d\n", ((Alien *)self)->age); } // { ... }
//
// new virtual function
int is_alien_et(Alien *self) // int is_alien()
{ return 0; } // { ... }
static const AlienVTable alien_vtable = {
.super.base_vtable = &thing_vtable,
.super.print_name = print_alien_name,
.super.print_age = print_alien_age,
.is_et = is_alien_et
};
void construct_alien(Alien *self, const char *name, int age)
{
construct_thing(&self->super, name); // Alien::Alien(const char *name, int age)
self->super.vtable = (ThingVTable *)&alien_vtable; // : Thing(name),
self->age = age; // age(age)
} //
//
void destruct_alien(Alien *self) // Alien::~Alien()
{ // { ... }
self->super.vtable = &thing_vtable;
destruct_thing(&self->super);
}
Alien *new_alien(const char *name, int age) // Alien *q = new Alien(name, age);
{ //
Alien *self = malloc(sizeof(Alien)); //
if (self == NULL) return NULL; //
//
construct_alien(self, name, age); //
return self; //
} //
//
void delete_alien(Alien *self) // delete q;
{
destruct_alien(self);
free(self);
}
int main(void) {
Thing thing; // not allowed in C++ since Thing is an abstract class
construct_thing(&thing, "stack thing"); // Thing thing("stack thing");
thing.vtable->print_name(&thing); // thing.print_name();
// error: pure virtual call
// thing.vtable->print_age(&thing); // thing.print_age();
destruct_thing(&thing); /* destructor implicitly called at end of main */
printf("\n");
Alien *alien = new_alien("heap alien", 1234); // Alien *alien = new Alien("heap alien", 1234)
((ThingVTable *)((AlienVTable *)alien->super.vtable)->super.base_vtable)->print_name((Thing *)alien); // alien->Thing::print_name();
((AlienVTable *)alien->super.vtable)->super.print_name((Thing *)alien); // alien->print_name();
((AlienVTable *)alien->super.vtable)->super.print_age((Thing *)alien); // alien->print_age();
printf("Is Alien ET? %d\n", ((AlienVTable *)alien->super.vtable)->is_et(alien)); // alien->is_et();
delete_alien(alien); // delete alien;
printf("\n");
Thing *poly = (Thing *)new_alien("pointer to alien", 9786); // Thing *poly = new Alien("pointer to alien", 9786)
poly->vtable->print_name(poly); // poly->print_name();
poly->vtable->print_age(poly); // poly->print_age();
printf("Is Alien ET? %d\n", ((AlienVTable *)((Alien *)poly)->super.vtable)->is_et((Alien *)poly)); // poly->is_et();
delete_alien((Alien *)poly); // delete poly;
return 0;
}
Output https://godbolt.org/z/1vfz5b:
Thing name: stack thing
Thing name: heap alien
Alien name: heap alien
Alien age: 1234
Is Alien ET? 0
Alien name: pointer to alien
Alien age: 9786
Is Alien ET? 0
Notes:
-
both Alien
and AlienVTable
通过嵌入基类的实例作为第一个成员来模仿“继承”(更多信息请参见C 中的结构继承 https://stackoverflow.com/questions/1114349/struct-inheritance-in-c例如),这也使从指向派生类型的指针到指向基类型的指针的转换合法化;
-
代码could通过使用辅助宏和内联(或语言扩展,例如匿名的struct fields https://gcc.gnu.org/onlinedocs/gcc/Unnamed-Fields.html),但这里的目的是让内部机制完全暴露;
-
the destruct_
帮助者将是虚拟化并包含在其中的主要候选人vtable
.