C继承通过类型惩罚,没有包含?

时间:2013-09-25 01:03:46

标签: c inheritance struct type-punning

我需要在C中使用一些面向对象的特性,特别是继承。幸运的是,堆栈溢出有一些很好的参考,特别是这个Semi-inheritance in C: How does this snippet work?和这个Object-orientation in C。我们的想法是在派生类中包含基类的实例并对其进行类型转换,如下所示:

struct base {
    int x;
    int y;
};

struct derived {
    struct base super;
    int z;
};

struct derived d;
d.super.x = 1;
d.super.y = 2;
d.z = 3;
struct base b = (struct base *)&d;

这很好,但是对于深度继承树来说变得很麻烦 - 我将拥有大约5-6个“类”的链,我真的不想输入derived.super.super.super.super.super.super每时每刻。我希望的是我可以对前n个元素的结构进行类型转换,如下所示:

struct base {
    int x;
    int y;
};

struct derived {
    int x;
    int y;
    int z;
};

struct derived d;
d.x = 1;
d.y = 2;
d.z = 3;
struct base b = (struct base *)&d;

我已经在Visual Studio 2012附带的C编译器上测试了它并且它可以工作,但我不知道C标准是否真正保证了它。是否有人可能确定这是否可以?我不想写大量的代码只是为了发现它在如此基础的层面上被打破了。

3 个答案:

答案 0 :(得分:3)

你在这里描述的是一个完全可移植的结构,基本上可以保证通过语言设计工作,除了标准的作者认为没有必要明确要求编译器支持的东西这显然应该有效。 C89为联合指定了通用初始序列规则,而不是指向结构的指针,因为给定:

struct s1 {int x; int y; ... other stuff... };
struct s2 {int x; int y; ... other stuff... };
union u { struct s1 v1; struct s2 v2; };

收到struct s1*到外部对象的代码 union u*或malloc'ed对象可以合法地将其转换为union u* 如果它与那种类型对齐,它可以合法地投射结果 指向struct s2*的指针,使用struct s1*struct s2*访问权限的效果必须与通过v1v2访问联合的效果相同会员。因此,编译器使所有指示规则工作的唯一方法是将一个结构类型的指针转​​换为另一个类型的指针并使用该指针检查公共初始序列的成员将起作用。 / p>

不幸的是,编译器编写者说CIS规则仅适用于底层对象具有union类型的情况,尽管事实上这样的事情代表了非常罕见的用例(与存在union类型的情况相比)目的是让编译器知道指向结构的指针应该互换处理以便检查CIS),并且因为代码很少接收标识符的struct s1*struct s2* union u中的对象,他们认为应该允许他们忽略这种可能性。因此,即使上述声明可见,gcc也会假定永远不会使用struct s1*struct s2*访问CIS的成员。

答案 1 :(得分:-1)

struct的地址是其第一个元素的地址,保证。

答案 2 :(得分:-1)

通过使用指针,您始终可以在层次结构中的任何级别创建对基类的引用。如果你使用继承结构的某种描述,你可以生成作为构建步骤所需的“类定义”和工厂函数。

#include <stdio.h>
#include <stdlib.h>

struct foo_class {
  int a;
  int b;
};

struct bar_class {
  struct foo_class foo;
  struct foo_class* base;

  int c;
  int d;
};

struct gazonk_class {
  struct bar_class bar;
  struct bar_class* base;

  struct foo_class* Foo;

  int e;
  int f;
};

struct gazonk_class* gazonk_factory() {
  struct gazonk_class* new_instance = malloc(sizeof(struct gazonk_class));

  new_instance->bar.base = &new_instance->bar.foo;
  new_instance->base = &new_instance->bar;
  new_instance->Foo = &new_instance->bar.foo;

  return new_instance;
}

int main(int argc, char* argv[]) {
  struct gazonk_class* object = gazonk_factory();

  object->Foo->a = 1;
  object->Foo->b = 2;

  object->base->c = 3;
  object->base->d = 4;

  object->e = 5;
  object->f = 6;

  fprintf(stdout, "%d %d %d %d %d %d\n",
      object->base->base->a,
      object->base->base->b,
      object->base->c,
      object->base->d,
      object->e,
      object->f);

  return 0;
}

在这个例子中,您可以使用base指针来回溯或直接引用基类。