destruct_ 助手将成为虚拟化并包含在 vtable 中的主要候选者.This is a conceptual question to see how an OOP technique might be done in C. I know it's not practical or recommended and there are many languages that would work better for this, but I'm just seeing how it might be done as a beginner to C.Let's say I have a base object called Thing. It will have some data and a few functions. Then I want to add another sub object called Alien -- it will have all the Thing data/methods, but also one additional method for it. Here is an example of what I have now:#include<stdio.h>#include<stdlib.h>typedef struct VTable VTable;typedef struct Thing { const VTable *vtable; char* name;} Thing;typedef struct VTable { void (*print) (Thing* self);} VTable;void print_hello(Thing *self) {printf("Hello, %s", self->name);}static const VTable thing_vtable = { .print = print_hello};typedef struct Alien { const VTable *vtable; char* name; // add one more function to vtable -- should that be a stand-alone? do a second 'subclass vtable'? etc.} Alien;void alien_function(void) {printf("Alien");}Alien* init_alien(void){ Alien* alien = malloc(sizeof(Alien)); alien->vtable = &thing_vtable; /* alien->vtable->alien_function = alien_function; */ return alien;}int main(void) { Alien *alien = init_alien(); /* alien->vtable->alien_function(); */ return 0;}Here is the code in Compiler Explorer.What might be one way to add the 'extra methods' to the Alien type? 解决方案 In OP's example, struct Alien extends struct Thing and adds new "virtual" functions (in the sense of functions dynamically dispatched via vtables), or in other words the AlienVTable extends the base 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 */ }; };The following is one way to implement this (comments refer to the rough C++ equivalent of some of the constructs, though the C code does not exactly duplicate the C++ semantics).#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 functionvoid print_alien_name(Thing *self) // void print_name(){ printf("Alien name: %s\n", self->name); } // { ... } // // implementation of base pure virtual functionvoid print_alien_age(Thing *self) // void print_age(){ printf("Alien age: %d\n", ((Alien *)self)->age); } // { ... } // // new virtual functionint 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:Thing name: stack thingThing name: heap alienAlien name: heap alienAlien age: 1234Is Alien ET? 0Alien name: pointer to alienAlien age: 9786Is Alien ET? 0Notes:both Alien and AlienVTable mimic "inheritance" by embedding an instance of the base class as the first member (more about that at Struct Inheritance in C for example), which also legitimizes casts from pointers to a derived type to pointers to the base type;the code could be made friendlier and easier to write/follow by using helper macros and inlines (or language extensions such as anonymous struct fields), but the intention here was to leave the internal mechanics fully exposed;the destruct_ helpers would be prime candidates to make virtual and include in the vtable. 这篇关于添加额外的“方法"对于子类的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持! 11-02 19:50