c - 为子类添加额外的 "methods"

标签 c oop

这是一个概念性问题,旨在了解如何在 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;
}


这是code in Compiler Explorer .

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;
}

Output:

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

注释:

  • AlienAlienVTable 都通过嵌入基类的实例作为第一个成员来模仿“继承”(例如,在 Struct Inheritance in C 处有更多相关信息),这也使从指向派生类型的指针到指向基类型的指针的转换合法化;

  • 通过使用辅助宏和内联(或诸如 anonymous struct fields 之类的语言扩展),代码可以变得更友好、更容易编写/遵循,但这里的目的是保留内部机制完全暴露;

  • destruct_ 帮助器将是虚拟化并包含在 vtable 中的主要候选者。

关于c - 为子类添加额外的 "methods",我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66054278/

相关文章:

c - "Pointer to Function"语法在 C 中如何工作?

Java - 从多种不同类型中挑选一个 ArrayList

php - 将子类映射为其扩展父类

java - 无参数构造函数是否被认为是 OOP 中的最佳实践

c# - 如何通过实现适配器模式来隔离胖接口(interface)?

javascript - 什么是好的简约 Javascript 继承方法?

c - 在不使用 getpass (3) 的情况下在 C 中获取密码?

c++ - C 和 C++ 之间的缓冲区相同方法中的文件读取?

c - 在C-pset5 cs50中加载trie数据结构

c - 如何在 C 中按值将 vector 传递给函数