2012年6月3日星期日

虚继承的内存布局

一天我一同事向我推荐了一本书,讲C++对象模型的。
周五下班后,我和另一个同事做了个试验:

#include <stdio.h>
struct B{   char c;};
struct B1: virtual B{   char b1;};
struct B2: virtual B{   char b2;};
struct D : public B1, public B2, virtual B{ char d;};

int main()
{
    D _d;
    D* d = &_d;
    d->d = 'd';d->b1='1';d->b2='2';d->c='c';
    B2* b2 = d;
    B1* b1 = d;
    B* b = d;
    printf("sizeof(B), sizeof(B1), sizeof(B2), sizeof(D) = %d %d %d %d\n", sizeof(B), sizeof(B1), sizeof(B2), sizeof(D));
    printf("d %p %p %d %d %d %d\n", d, *(long*)d, long(&d->c) - long(d), long(&d->b1) - long(d), long(&d->b2) - long(d), long(&d->d)-long(d) );
    printf("b1 c,b1 = %c %c\n", b1->c, b1->b1);
    printf("b1 %p %p %d %d\n", b1, *(long*)b1, long(&b1->c) - long(b1), long(&b1->b1) - long(b1) );
    printf("b2 c,b2 = %c %c\n", b2->c, b2->b2);
    printf("b2 %p %p %d %d\n", b2, *(long*)b2, long(&b2->c) - long(b2), long(&b2->b2) - long(b2) );
    printf("b c %c\n", b->c);
    printf("b %p %d\n", b, long(&b->c)-long(b) );
    return 0;
}

g++编译后运行结果

sizeof(B), sizeof(B1), sizeof(B2), sizeof(D) = 1 16 16 32
d 0x7ffff7897d60 0x401178 26 8 24 25
b1 c,b1 = c 1
b1 0x7ffff7897d60 0x401178 26 8
b2 c,b2 = c 2
b2 0x7ffff7897d70 0x401190 10 8
b c c
b 0x7ffff7897d7a 0


貌似是有32个字节的连续内存。d和b1都指向这段内存的首地址,b2指向偏移量为16字节的位置。b指向偏移量为26字节的位置。
前16个字节:前8个字节是B1的虚表的指针,第9个字节存了成员b1,然后是7个字节的padding。
后16个字节:前8个字节是B2的虚表的指针,第9个字节存了成员b2,第10个字节存了成员d,第11个字节存了成员c,然后是5个字节的padding。
内存布局的顺序大约是:第一个父类成员、第二个父类成员、当前类成员、公共虚继承的祖父类成员。
另外一个发现是:指针的类型转换后可能导致指针本身的值的变化,因为子类指针和父类指针可能指向同一段连续内存的不同位置。


没有评论: