c - C 中的继承和方法重写 - 如何使其定义行为

标签 c oop language-lawyer dynamic-cast strict-aliasing

我有自定义的小型 OOP 式继承功能,如下所示:

// base class
struct BaseTag;

typedef struct {
    int (*DoAwesomeStuff)(struct BaseTag* pInstance);
} S_BaseVtable;

typedef struct BaseTag{
    S_BaseVtable* pVtable;
    int AwesomeValue;
} S_Base;

// child class
struct ChildTag;

typedef struct {
    S_BaseVtable Base;
    void (*SomeOtherStuff)(struct ChildTag* pInstance);
} S_ChildVTable;

typedef struct ChildTag {
    S_Base BaseClass;
    int EvenAwesomerValue;
} S_Child;

现在假设我有一个子类构造函数,其中基类 vtable 被子 vtable 覆盖:

void Child_ctor(S_Child* pInstance) {
    Base_ctor((S_Base*) pInstance);
    pInstance.BaseClass.pVtable = (S_BaseVtable*) &MyChildVTable;
}

此外,在这个子虚函数表中,我想使用如下方法重写基类中的 DoAwesomeStuff() 方法:

int Child_DoAwesomeStuff(struct BaseTag* pInstance) {
    S_Child* pChild = (S_Child*) pInstance; // undefined behaviour
    return pChild->EvenAwesomerValue;
}

我偶尔会看到这种模式的变化,但我发现它存在一些问题。我的主要问题是

  • 如何从隐藏在 S_BaseVtable 指针后面的子实例访问 S_ChildVtable
  • 如何正确地将 Child_DoAwesomeStuff()pInstance 参数转换为 S_Child* 类型?

据我了解 C 标准,从 S_Child* 转换为 S_Base* (以及相应的 vtable 类型)作为 的第一个成员是可以的S_Child 是一个 S_Base 实例。但反之亦然,这是未定义的行为。

类似 S_Child* pChild = (S_Child*)((char*) pInstance) 的内容合法且已定义吗?


编辑

我的问题有点不清楚且具有误导性。 我认为 UB 并不是强制转换本身,而是从 pInstance 强制转换后取消引用 pChild。

我再次浏览了 C11 标准以寻找一些引用,但对我来说不再那么清楚了。

6.3.2.3/7:

A pointer to an object type may be converted to a pointer to a different object type. If the resulting pointer is not correctly aligned (68) for the referenced type, the behavior is undefined. Otherwise, when converted back again, the result shall compare equal to the original pointer.

所以我想我的问题实际上是 - 需要采取什么机制才能确保 S_Base 和 S_Child 正确对齐?

最佳答案

So I guess my question really is - What mechanics need to be in place so that it is ensured that S_Base and S_Child are correctly aligned?

TL;DR:不需要特殊的机制来涵盖指向继承框架中有效的类型的指针之间的转换。


对齐在 C17 6.2.8“对象对齐”中进行了描述,并在规范的许多其他地方进行了涉及。

虽然语言规范没有明确说明这个问题,但我们可以观察到结构类型的对齐要求必须至少与其最严格对齐的成员一样严格,否则实现无法确保所有成员所有实例的都将正确对齐。由于您的 S_Child 具有 S_Base 类型的成员,因此前者的对齐要求不能比后者弱,因此需要转换有效的 S_Child *输入 S_Base * 永远不会与不正确的对齐方式发生冲突。

S_Child 可能比 S_Base 具有更严格的对齐要求,但这不是您在实践中需要担心的问题。将 S_Base * 转换为在继承系统中语义上有效的类型 S_Child * 的唯一情况是原始 S_Base * 指向S_Child 的第一个成员。在这种情况下,您可以相信

A pointer to a structure object, suitably converted, points to its initial member (or if that member is a bit-field, then to the unit in which it resides), and vice versa.

(C17 6.7.2.1/15)

当然,这适用于两个方向,因此它为 S_Child *S_Base * 情况提供了额外(甚至更好)的支持。

几乎同样的事情也适用于您的 vtable 方面,因为您正在以类似于数据成员结构的方式构建它们。

Would something like S_Child* pChild = (S_Child*)((char*) pInstance) be legal and defined?

如果pInstance是指向任何对象类型的有效指针,则转换为char *是有效的,但它不会为您带来任何关于结果转换的信息输入S_Child *。你不妨直接写

S_Child *pChild = (S_Child *) pInstance;

,这在您(应该)关心的所有情况下都非常好。

关于c - C 中的继承和方法重写 - 如何使其定义行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/75902664/

相关文章:

c++ - std::less 枚举

c - 哪一部分导致浮点异常?

c - 尽管出现警告消息 'not found",但已成功删除内核模块

c# - 基于示例的关于继承的混淆

c++ - C++11 标准中的核心常量表达式是什么?

c - 为什么在C99中SCHAR_MIN定义为-127?

c - free() 不从结构数组中释放字符串

c# - 如何确定一个位序列是正数还是负数

PHP 类的私有(private)属性和方法

language-agnostic - 比较对象时 null == null 是否应该为真?