为子类添加额外的“方法”

2024-03-21

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


这里是编译器资源管理器中的代码 https://godbolt.org/z/oKxvoM.

将“额外方法”添加到的一种方法可能是什么Alien type?


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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为子类添加额外的“方法” 的相关文章

  • 在动态事件处理程序中引用“this”

    在我的 myClass 类中 我使用 Reflection Emit 为 myClass 类成员之一动态编写事件处理程序 我已经成功地做到了这一点 现在 我想修改事件处理程序以调用 myClass 类中的实例方法之一 但是 我无法弄清楚如何
  • 如何将包含 5000 条记录的 Excel 文件插入到 documentDB 中?

    我有一个 Excel 文件 最初约有 200 行 我能够将 Excel 文件转换为数据表 并且所有内容都正确插入到 documentdb 中 Excel 文件现在有 5000 行 在插入 30 40 条记录后不会插入 其余所有行不会插入到
  • Accept() 是线程安全的吗?

    我目前正在用 C 语言为我正在做的课程编写一个简单的网络服务器 我们的一项要求是实现一个线程池来使用 pthread 处理连接 我知道我将如何粗略地执行此操作 在主线程中调用accept并将文件描述符传递给freee线程 但是我的朋友建议了
  • 如何从RichTextBox中获取显示的文本?

    如何获得显示的RichTextBox 中的文本 我的意思是 如果 RichTextBox 滚动到末尾 我只想接收那些对我来说可见的行 P S 获得第一个显示的字符串就足够了 您想使用 RichTextBox GetCharIndexFrom
  • 如何使用 MVVM 更新 WPF 中编辑的数据? [复制]

    这个问题在这里已经有答案了 我正在为聊天应用程序构建 UI 设计 在尝试更新所选联系人的消息时遇到问题 选择现有联系人 选择编辑选项 然后编辑其属性 例如用户名和图像 后 唯一进行的更改是联系人的用户名和图像 我仍然想更改 MessageM
  • 静态类变量与外部变量相同,只是具有类作用域吗?

    在我看来 静态类变量与外部变量相同 因为你只需要declare它在static int x extern int x语句 并在其他地方实际定义它 通常在 cpp 文件中 静态类变量 h file class Foo static int x
  • 访问“if”语句之外的变量

    我怎样才能使insuranceCost以外可用if陈述 if this comboBox5 Text Third Party Fire and Theft double insuranceCost 1 在 if 语句之外定义它 double
  • 无法从 Web api POST 读取正文数据

    我正在尝试从新的 Asp Net Web Api 中的请求中提取一些数据 我有一个像这样的处理程序设置 public class MyTestHandler DelegatingHandler protected override Syst
  • 导出类时编译器错误

    我正在使用 Visual Studio 2013 但遇到了一个奇怪的问题 当我导出一个类时 它会抛出 尝试引用已删除的函数 错误 但是 当该类未导出时 它的行为会正确 让我举个例子 class Foo note the export cla
  • Paradox 表 - Oledb 异常:外部表不是预期的格式

    我正在使用 Oledb 从 Paradox 表中读取一些数据 我遇到的问题是 当我将代码复制到控制台应用程序时 代码可以工作 但在 WinForms 中却不行 两者都以 x86 进行调试 我实际上只是复制代码 在 WinForms 应用程序
  • 方法“xxx”不能是事件的方法,因为该类派生的类已经定义了该方法

    我有一个代码 public class Layout UserControl protected void DisplayX DisplayClicked object sender DisplayEventArgs e CurrentDi
  • 是否有像 gccxml 这样的用于生成包装器的 C 标头解析器工具?

    我需要为一种新的编程语言编写一些 C 标头包装器 并且想要类似 gccxml 的东西 但不完全依赖 gcc 以及它在 Windows 系统上带来的问题 只需要读C而不是C 只要有完整的文档记录 任何格式的输出都可以 Linux Solari
  • 存储过程上的 OdbcCommand - 输出参数上出现“未提供参数”错误

    我正在尝试执行存储过程 通过 ODBC 驱动程序针对 SQL Server 2005 但收到以下错误 过程或函数 GetNodeID 需要参数 ID 但未提供该参数 ID 是我的过程的 OUTPUT 参数 在存储过程中指定了一个输入 mac
  • 在 MATLAB 中创建共享库

    一位研究人员在 MATLAB 中创建了一个小型仿真 我们希望其他人也能使用它 我的计划是进行模拟 清理一些东西并将其变成一组函数 然后我打算将其编译成C库并使用SWIG https en wikipedia org wiki SWIG创建一
  • 默认值 C# 类 [重复]

    这个问题在这里已经有答案了 我在控制器中有一个函数 并且我收到表单的信息 我有这个代码 public Actionresult functionOne string a string b string c foo 我尝试将其转换为类似的类
  • .NET JIT 编译的代码缓存在哪里?

    NET 程序首先被编译为 MSIL 代码 当它被执行时 JIT编译器会将其编译为本机机器代码 我想知道 这些JIT编译的机器代码存储在哪里 它只存储在进程的地址空间中吗 但由于程序的第二次启动比第一次快得多 我认为即使在执行完成后 该本机代
  • 如何使用收益返回和递归获得字母的每个组合?

    我有几个像这样的字符串列表 可能有几十个列表 1 A B C 2 1 2 3 3 D E F 这三个仅作为示例 用户可以从几十个具有不同数量元素的类似列表中进行选择 再举个例子 这对于用户来说也是一个完全有效的选择 25 empty 4 1
  • TPL 数据流块下游如何获取源生成的数据?

    我正在使用 TPL Dataflow 处理图像 我收到处理请求 从流中读取图像 应用多次转换 然后将生成的图像写入另一个流 Request gt Stream gt Image gt Image gt Stream 为此 我使用块 Buff
  • g++ C++0x 枚举类编译器警告

    我一直在将可怕的 C 类型安全伪枚举重构为新的 C 0x 类型安全枚举 因为它们是way更具可读性 不管怎样 我在导出的类中使用它们 所以我明确地将它们标记为导出 enum class attribute visibility defaul
  • 启动画面后主窗口出现在其他窗口后面

    我有一个带有启动屏幕的 Windows 窗体应用程序 当我运行该应用程序时 启动屏幕显示正常 消失并加载应用程序的主窗体 但是 当我加载主窗体时 它出现在包含该应用程序的 Windows 资源管理器目录下 这是运行启动画面然后运行主窗体的代

随机推荐

  • 使用 JavaScript 测试 url 可用性

    是否可以通过 jQuery 或纯 JavaScript 测试另一个域上的网页是否可用 我尝试使用 ajax 调用获取响应标头 但无论我测试自己的域之外的哪个网站 我都会收到错误 那么我的服务器上真的需要代理脚本吗 或者我可以跳过该请求吗 是
  • 如何为邮递员请求生成未来的日期和时间

    name IronMan phone number 555555555 number of guest 10 tables 2 reservation start at 2020 10 15T10 00 00 861873Z reserva
  • 按行组合 pandas 数据帧的有效方法

    我有 14 个数据框 每个数据框有 14 列和超过 250 000 行 数据框具有相同的列标题 我想按行合并数据框 我尝试将数据帧连接到 不断增长的 数据帧 这需要几个小时 本质上 我做了如下 13 次这样的事情 DF pd DataFra
  • 应用“文本溢出:省略号;”到内部 div

    I have a div structure similar to following image 我需要应用 文本溢出 省略号 在较小的窗口中使用此 div 结构中的文本 图 2 但它并没有像我预期的那样适用 这是我的 HTML 代码 h
  • Android 的所选项目选择器不起作用

    我有一个列表视图 我想在其中以自定义方式突出显示所选项目 我正在设置适配器中的每个项目属性getView方法 包括itemView setSelected true 主布局通过以下方式定义列表视图
  • C# 比较两个不同对象的列表[重复]

    这个问题在这里已经有答案了 I saw 比较两个 List 的最快方法 https stackoverflow com questions 12795882 quickest way to compare two list但我很难使其适应我
  • 级联空引用异常检查?

    有没有办法在 C 中进行通用级联空引用检查 我想要实现的是 如果我试图访问一个字符串变量 它是类 C 的一部分 而类 C 又在类 B 中 而类 B 又在 A 中 ABC str 如果我传入 A 我必须检查 A 是否为空 然后检查 B 是否为
  • 使用 smack 库的 GCM NoResponseException:回复超时内未收到响应

    我是 gcm 新手 我尝试使用 Smack API 连接到 GCM 的云连接服务器 XMPP 一开始还好 我的代码 uid 123456789 apiKey A B XMPPTCPConnectionConfiguration Builde
  • 当其中任意 1 个组合框发生更改时,列表框中的所有组合框都会发生变化

    我有一个ListBox在绑定到的表单上ObservableCollection自定义类型 每个项目内都有一个ComboBox绑定到枚举类型 当窗口加载时 所有ComboBoxes 默认为某个值 当我改变SelectedItem对于任何一个
  • 如何设置“git集群”

    我们正在考虑将我们的源代码版本控制系统从 Subversion 更改为 Git 我们正在突破当前设置的限制 并认为使用 git 会更好 我们一直在办公室使用主服务器 并在数据中心运行镜像 镜像配置为代理传入主服务器的提交 这样 两台机器就像
  • 为什么 emacs 中的 vc-git 和 magit 忽略我的 git 配置设置?

    我正在使用 magit 运行 emacs 24 1 1 的官方 Windows 二进制文件 当我提交文件时 日志中显示的用户名和电子邮件将从我的帐户和计算机名称中删除 而不是使用全局 git 配置或此重现的本地设置中设置的值 从 git b
  • 如何向数组添加元素?

    如何向现有数组添加元素 我有数组 Array 0 gt Array user id gt 1 user login gt Saibamen user password gt 4028a0e356acc947fcd2bfshfh3w26gds
  • SDL2 升起窗口而不给予焦点

    我需要在窗口上显示工具提示 我正在使用工具提示创建第二个窗口 并使用 SDL RaiseWindow 将其带到顶部 然而 这样做会导致工具提示窃取焦点 这不是我想要的 有没有办法在不改变焦点的情况下将窗口置于顶部 另外 有没有办法在不改变窗
  • inDither = true Android

    有人可以解释一下设置时到底发生了什么吗抖动 true在 Android 中配置位图的上下文中 在 Developer Android 上可以阅读有关静态变量的信息 Config RGB 565 根据源的配置 此配置可能会产生轻微的视觉伪影
  • 为什么Java的SimpleDateFormat不是线程安全的? [复制]

    这个问题在这里已经有答案了 请用代码示例说明为什么 SimpleDateFormat 不是线程安全的 这堂课有什么问题 是SimpleDateFormat的格式化函数的问题 请给出在课堂上演示此错误的代码 FastDateFormat 是线
  • 如何在PyUsb中绑定/取消绑定USB设备?

    我需要在 python 脚本上打开 关闭多个 USB 设备 我可以使用 PyUsb 绑定和取消绑定 USB 设备吗 我可以使用 shell 命令来做到这一点 关闭电源 echo device nuber gt sys bus usb dri
  • SQLite - 表约束唯一和列约束唯一之间有什么区别?

    关于 SQLite 的问题 在 CREATE TABLE SQL 中 我们可以通过以下任一方式添加 UNIQUE 约束 列约束或表约束 我的问题很简单 他们的工作方式不同吗 我能发现的唯一区别是 在表约束中 可能有多个indexed col
  • 找出 MySQL 在 Mac OS X 上的安装位置

    如何找出 Mac OS X 10 7 9 上 MySQL 的安装位置 我安装了 MAMP 所以我认为它与此安装捆绑在一起 要检查 MAMP 的 MySQL 版本 请在终端中使用以下命令 Applications MAMP Library b
  • DOCX w:t(文本)元素跨越多个 w:r(运行)元素?

    我们编写了一个软件 可以从 Word 文档的内部 XML 文件中处理 XML 并用替换值替换某些代码 有时我们发现此类代码在多次运行之间被破坏 以下是我们有时会遇到的此类情况的示例
  • 为子类添加额外的“方法”

    这是一个概念性问题 旨在了解如何在 C 中完成 OOP 技术 我知道它不实用或不推荐 并且有许多语言可以更好地实现此目的 但我只是看看作为初学者如何完成它到 C 假设我有一个名为的基础对象Thing 它将有一些数据和一些功能 然后我想添加另