C,结构指针多态性

时间:2012-08-18 13:25:19

标签: c inheritance polymorphism

注意:这不是C ++问题,我不能使用C ++编译器,只能使用C99。

这是有效的(可接受的,漂亮的)代码吗?

typedef struct sA{
   int a;
} A;

typedef struct aB{
   struct sA a;
   int b;
} B;

A aaa;
B bbb;

void init(){
   bbb.b=10;
   bbb.a.a=20;
   set((A*)&bbb);
}

void set(A* a){
   aaa=*a;
}

void useLikeB(){
   printf("B.b = %d", ((B*)&aaa)->b);
}

简而言之,当我需要指定的行为时,将“子类”强制转换为“超类”并重铸“超类”到“子类”是否有效?

由于

4 个答案:

答案 0 :(得分:5)

首先,C99标准允许您将任何结构指针强制转换为指向其第一个成员的指针,另一种方式( 6.7.2.1结构和联合说明符):

  

13在结构对象中,非位字段成员和位域所在的单元具有按声明顺序增加的地址。指向适当转换的结构对象的指针指向其初始成员(或者如果该成员是位字段,则指向它所在的单元),反之亦然。在结构对象中可能有未命名的填充,但不是在它的开头。

换句话说,在您的代码中,您可以自由地使用:

  1. B*转换为A* - 它将始终正常运行,
  2. A*转换为B* - 但如果它实际上没有指向B,那么您将获得访问其他成员的随机失败,
  3. 将指向A*的结构分配给A - 但如果指针是从B*转换而来,则只会分配公共成员,B的其余成员将被忽略,
  4. 将指向B*的结构分配给A - 但您必须先转换指针,然后注意(3)。
  5. 因此,您的示例几乎正确。但是useLikeB()无法正常工作,因为aaaA类型的结构,您指定的结构如第(4)节所述。这有两个结果:

    1. 非常见的B成员实际上不会被复制到aaa(如(3)所述),
    2. 您的程序将无法随机尝试访问A B,而不是A(您正在访问不存在的成员,如(2)中所述)。
    3. 要以更实际的方式解释这一点,当您声明A编译器保留保留B所有成员所需的内存量时。 A拥有更多成员,因此需要更多内存。由于B是常规变量,因此无法在运行时更改其大小,因此无法保留{{1}}的其余成员。


      作为一个注释,通过(1)你实际上可以指向成员而不是转换更好的指针,它将允许你访问任何成员,而不仅仅是第一个成员。但请注意,在这种情况下,相反的情况将不再起作用!

答案 1 :(得分:2)

我认为这很脏并且相对危险。你想用这个来实现什么?也不能保证aaa是B,它也可能是A.所以当有人称之为“uselikeB”时,它可能会失败。同样取决于体系结构“int a”和“指向struct a的指针”可能正确或不正确重叠,并且当您分配给“int a”然后访问“struct a”时可能会导致有趣的事情发生

答案 2 :(得分:0)

你为什么要这样做?具有

set((A*)&bbb);

比正确的

更难写
set(&bbb.a);

您在此处发帖时应该避免的其他事项:

  • 在声明
  • 之前使用set
  • aaa=a应为aaa = *a

答案 3 :(得分:0)

首先,我同意以前海报中关于这项任务安全的大多数问题。

话虽如此,如果你需要走那条路,我会添加一个间接级别和一些类型安全检查器。

static const int struct_a_id = 1;
static const int struct_b_id = 2;
struct MyStructPtr {
   int type;
   union {
     A* ptra;
     B* ptrb;
     //continue if you have more types.
   }
};

这个想法是你通过传递一个包含一些“类型”信息的结构来管理你的指针。您可以在描述类树的一侧构建一个类树(请注意,考虑到安全构建的限制,可以使用树来表示)并且能够回答问题以确保您正确地上下构建结构。所以你的“useLikeB”函数可以这样写。

MyStructPtr the_ptr;
void init_ptr(A* pa)
{
     the_ptr.type = struct_a_id 
     the_ptr.ptra = pa;
}
void useLikeB(){
    //This function should FAIL IF aaa CANT BE SAFELY CASTED TO B
    //by checking in your type tree that the a type is below the 
    //a type (not necesarily a direct children).
    assert( is_castable_to(the_ptr.type,struct_b_id ) ); 
    printf("B.b = %d", the_ptr.ptrb->b);
}

我的2美分。