这是一个概念性问题,旨在了解如何在 C 中完成 OOP 技术。我知道它不实用或不推荐,并且有许多语言可以更好地实现此目的,但我只是看看如何完成它作为 C 语言的初学者。
假设我有一个名为 Thing
的基础对象。它将有一些数据和一些功能。然后我想添加另一个名为 Alien
的子对象 - 它将拥有所有 Thing
数据/方法,而且还有一个附加方法。这是我现在拥有的示例:
#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;
}
向 Alien
类型添加“额外方法”的一种方法是什么?
最佳答案
在OP的示例中,struct Alien
扩展了struct Thing
并添加了新的“虚拟”函数(在通过vtables动态调度的函数的意义上) ),或者换句话说,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 代码不完全复制 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;
}
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
注释:
Alien
和AlienVTable
都通过嵌入基类的实例作为第一个成员来模仿“继承”(例如,在 Struct Inheritance in C 处有更多相关信息),这也使从指向派生类型的指针到指向基类型的指针的转换合法化;通过使用辅助宏和内联(或诸如 anonymous
struct
fields 之类的语言扩展),代码可以变得更友好、更容易编写/遵循,但这里的目的是保留内部机制完全暴露;destruct_
帮助器将是虚拟化并包含在vtable
中的主要候选者。
关于c - 为子类添加额外的 "methods",我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66054278/