c-为在C中实现多态性是否需要分配不兼容的指针

我尝试使用以下代码在C中模拟C的多态性:

#include<stdio.h>

typedef struct Base {
  void (*out) (void);
} Base;

typedef struct Derived {
  Base base;
  int x;
} Derived;

void base_out() {
  printf("base\n");
}

void derived_out() {
  printf("derived\n");
}

void foo(void) {
  Derived d;
  Base b;
  b.out = base_out;
  d.base.out = derived_out;

  Base *p;
  p = &b;
  p->out();
  //warning: assignment from incompatible pointer type
  p = &d;
  p->out();
}

int main(void) {
  foo();
}

但是当p =& d时,我必须进行不兼容的指针分配.为了使其正常工作,“派生”中的基本成员也必须是第一个成员.如何避免两者?

解决方法:

不需要不兼容的指针分配.根据C 2011 [草稿N1570] 6.7.2.1 15,“指向经过适当转换的结构对象的指针指向其初始成员(或者,如果该成员是位字段,则指向它所驻留的单元),并且因此,当Derived的第一个元素是基本对象时,指向Derived的指针可以转换为指向Base的指针.并且,可以将指向作为派生对象中的第一个成员的Base的指针转换为指向派生对象的指针.

所以这是合法的.编译器可能会警告您它只是为了安全起见,因为这很罕见.使用显式强制转换可以避免这种情况:

p = (Base *) &d;

或者,您可以直接获取基地的地址:

p = &d.base;

但是,从基准返回到派生仍然需要强制转换:

Derived *x = (Derived *) p;

至于将基地放在第一位成员之外的其他地方,那么您将处于更不稳定的境地.派生到基本方向仍然很容易. p =& d.base很好.换句话说,这可能与合法的C尽可能接近:

>获取指向基数的字符类型指针:char * c =(char *)p.在C语言中,将指向对象的任何指针转换为指向字符类型的指针是合法的,这为您提供了指向该对象的第一个字节的指针.
>从Derived的开头减去Base成员的字节数:c-= offsetof(Derived,base). (这需要包含< stddef.h>.)由于c指向Derived中Base的第一个字节,因此它指向Derived中的一个字节,并且您大部分(参见下文)被允许将Derived视为字节数组. ,因此我们可以减去以回到开头. offsetof宏为此提供了字节数.
>将字符指针转换为Derived的指针:Derived * x =(Derived *)c.在这里,我们处境不稳.如果将指针从“派生”转换为字符指针,则可以立即将其转换回.但是我们没有以这种方式获得此指针.它指向同一个字节,但是对我来说C标准是否支持此字节尚不清楚. (实际上,上面的步骤2有一个类似的问题;一个对象可以解释为字符数组,但是我们从不确定C标准实际上是否支持的方向来解决这个问题.)

将它们放在一起,转换将是:

Derived *x = (Derived *) ((char *) p - offsetof(Derived, base));

考虑到支持的不确定性,除非在C实现中保证我正在使用支持此指针算法的功能,否则我只能在实验代码中或在课堂练习中使用它.最好坚持使用base作为第一个元素,因为这些转换严格符合C.

上一篇:c – 如何从基类转换为派生类?


下一篇:c – 基类指针vs继承类指针?