4

我现在需要在 C 中获得一些面向对象的特性,特别是继承。幸运的是,有一些关于堆栈溢出的很好的参考资料,特别是C 中的半继承:这个片段如何工作?和 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 标准是否真的保证它。有没有人可以确定这是否可以?我不想编写大量代码只是为了发现它在如此基本的层面上被破坏了。

4

3 回答 3

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

将 a 接收struct s1*到外部对象的代码union u*可以合法地将其强制转换为 aunion u* 如果它与该类型对齐,并且它可以合法地将结果指针强制转换为struct s2*,以及使用访问struct s1*或访问的效果struct s2*必须与通过v1orv2成员访问联合相同。因此,编译器使所有指示的规则起作用的唯一方法是说将一种结构类型的指针转​​换为另一种类型的指针并使用该指针检查公共初始序列的成员将起作用。

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

于 2016-09-02T21:34:42.963 回答
-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指针返回或直接引用基类。

于 2013-09-25T06:10:47.777 回答
-1

astruct的地址是它的第一个元素的地址,保证。

于 2013-09-25T01:09:12.310 回答