添加额外的“方法"对于子类 [英] Add additional "methods" for a subclass

查看:42
本文介绍了添加额外的“方法"对于子类的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是一个概念性问题,用于了解如何在 C 中完成 OOP 技术.我知道这不实用或不推荐,并且有许多语言可以更好地解决此问题,但我只是看看它是如何完成的作为 C 的初学者.

假设我有一个名为 Thing 的基础对象.它将有一些数据和一些功能.然后我想添加另一个名为 Alien 的子对象——它将拥有所有 Thing 数据/方法,但还有一个额外的方法.这是我现在拥有的示例:

#include#includetypedef struct VTable VTable;typedef struct 东西{const VTable *vtable;字符*名称;} 事物;typedef struct VTable {void (*print) (Thing* self);表;void print_hello(Thing *self) {printf("Hello, %s", self->name);}静态 const VTable thing_vtable = {.print = print_hello};typedef struct 外星人 {const VTable *vtable;字符*名称;//再向 vtable 添加一个函数——它应该是一个独立的函数吗?做第二个子类 vtable"?等等.外星人;无效外星人功能(无效){printf(外星人");}外星人* init_alien(void){外星人 * 外星人 = malloc(sizeof(Alien));外星人-> vtable = &thing_vtable;/* 外星人->vtable->alien_function = Alien_function;*/返回外星人;}int main(void) {外星人 *alien = init_alien();/* 外星人->vtable->alien_function();*/返回0;}

这是编译器资源管理器中的代码.

将额外方法"添加到 Alien 类型的一种方法是什么?

解决方案

在 OP 的示例中,struct Alien 扩展了 struct Thing 并添加了新的virtual"函数(通过 vtables 动态调度的函数),或者换句话说 AlienVTable 扩展了基础 ThingVTable.

 struct Thing { struct Alien {---\/---/--- VTable *vtable;||虚表 *虚表;----\|字符 *名称;|--->|字符 *名称;||---/\--- ||};/* 其他外星人成员*/||};||||-->struct ThingVTable { struct AlienVTable { <---|---\/---void (*print)(Thing *self);|--->|void (*print)(Thing *self);---/\---void (*function)(外星人 *self);/* 其他虚拟 Alien 函数 */};};

以下是实现这一点的一种方法(注释指的是某些构造的粗略等效的 C++,尽管 C 代码完全复制 C++ 语义).

#ifdef _MSC_VER#define _CRT_NONSTDC_NO_DEPRECATE//msvc strdup c4996#万一#include #include #include 

typedef struct Thing {//struct Thing {const struct ThingVTable *vtable;//字符 *名称;//字符 *名称;} 事物;////typedef struct ThingVTable {//const void *base_vtable;//void (*print_name)(东西 *self);//虚拟 void print_name();void (*print_age)(东西 *self);//虚拟无效 print_age() = 0;} ThingVTable;//};void print_thing_name(Thing *self)//void Thing::print_name(){ printf("事物名称:%s\n", self->name);}//{ ... }静态 const ThingVTable thing_vtable = {.base_vtable = NULL,.print_name = print_thing_name,.print_age = NULL};void constructor_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(){//{ ... }免费(自我->姓名);self-> vtable = NULL;}Thing *new_thing(const char *name)//Thing *p = new Thing(name);{//Thing *self = malloc(sizeof(Thing));//if (self == NULL) 返回 NULL;////构造物(自我,名称);//回归自我;//}////void delete_thing(Thing *self)//删除 p;{破坏事物(自我);自由(自我);}

typedef struct Alien {//struct Alien : Thing {东西超级;//年龄;//整数年龄;外星人;////typedef struct AlienVTable {//void print_name() override;ThingVTable 超级;//void print_age() 覆盖;int (*is_et)(struct Alien *);//虚拟 int is_et();//};} AlienVTable;//覆盖基础虚函数void print_alien_name(Thing *self)//void print_name(){ printf("外星人姓名:%s\n", self->name);}//{ ... }////基础纯虚函数的实现void print_alien_age(Thing *self)//void print_age(){ printf("外星人年龄:%d\n", ((Alien *)self)->age);}//{ ... }////新的虚函数int is_alien_et(Alien *self)//int is_alien(){ 返回 0;}//{ ... }静态常量 AlienVTable 外星人_vtable = {.super.base_vtable = &thing_vtable,.super.print_name = print_alien_name,.super.print_age = print_alien_age,.is_et = is_alien_et};void constructor_alien(Alien *self, const char *name, int age){构造物(&self->super, name);//Alien::Alien(const char *name, int age)self->super.vtable = (ThingVTable *)&alien_vtable;//: 东西(名称),自我>年龄=年龄;//年龄(年龄)}////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);{//外星人 *self = malloc(sizeof(Alien));//if (self == NULL) 返回 NULL;////构造外星人(自我,姓名,年龄);//回归自我;//}////void delete_alien(Alien *self)//删除 q;{destruct_alien(自我);自由(自我);}

int main(void) {东西;//在 C++ 中不允许,因为 Thing 是一个抽象类构造事物(&事物,堆栈事物");//事物事物(堆栈事物");thing.vtable->print_name(&thing);//thing.print_name();//错误:纯虚调用//thing.vtable->print_age(&thing);//thing.print_age();destruct_thing(&thing);/* 在 main 的末尾隐式调用析构函数 */printf("\n");Alien *alien = new_alien(堆外星人", 1234);//Alien *alien = new Alien(堆外星人", 1234)((ThingVTable *)((AlienVTable *)alien->super.vtable)->super.base_vtable)->print_name((Thing *)alien);//外星人->事物::print_name();((AlienVTable *)alien->super.vtable)->super.print_name((Thing *)alien);//外星人->print_name();((AlienVTable *)alien->super.vtable)->super.print_age((Thing *)alien);//外星人->print_age();printf("Is Alien ET? %d\n", ((AlienVTable *)alien->super.vtable)->is_et(alien));//外星人->is_et();delete_alien(外星人);//删除外星人;printf("\n");Thing *poly = (Thing *)new_alien(指向外星人的指针", 9786);//Thing *poly = new 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);//删除多边形;返回0;}

输出:

事物名称:栈事物事物名称:堆外星人外星人名称:堆外星人外星人年龄:1234外星人是外星人吗?0外星人名称:指向外星人的指针外星人年龄:9786外星人是外星人吗?0

注意事项:

  • AlienAlienVTable 都模仿继承"通过嵌入基类的实例作为第一个成员(更多信息请参见 C 中的结构继承 例如),这也使从指向派生类型的指针到指向基类型的指针的转换合法化;

  • 代码可以通过使用辅助宏和内联(或诸如 匿名struct 字段),但这里的目的是让内部机制完全暴露;

  • 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 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

Notes:

  • 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.

这篇关于添加额外的“方法"对于子类的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆