为什么具有虚拟继承的派生类的大小会出乎意料?

时间:2014-07-24 09:23:33

标签: c++ inheritance

由于空类的大小是1个字节但是这个空类实际上是继承的(在gcc编译器上)sizeof(derived1)的答案是8字节......,为什么会发生这种情况因为sizeof(derived1)必须是sizeof(char)+sizeof(virtual pointer),这是1 + 4..ie 5字节。所以,从这个额外的3个字节到来的地方...... ???

#include <iostream>
using namespace std;

class Empty
{};

class Derived1 : virtual public Empty<br>
{
    char c;
};

int main()
{
    cout << "sizeof(Empty) " << sizeof(Empty) << endl;
    cout << "sizeof(Derived1) " << sizeof(Derived1) << endl;
    return 0;
}

2 个答案:

答案 0 :(得分:5)

就C ++标准而言,它都未指定, 但通常虚拟继承会添加一个或两个指针 反过来又施加了额外的对齐限制;的大小 具有虚拟继承或虚函数的类 几乎肯定必须是指针大小的倍数 (大多数系统都是4或8)。

答案 1 :(得分:2)

问题在于结构内存对齐。有关详细信息,请查看此atricle:http://en.wikipedia.org/wiki/Data_structure_alignment 正如您所看到的,内存填充取决于您的平台。

说明:

让我们尝试调试用于您班级的内存。 我们知道变量不是虚拟。这意味着没有vintrual表的记录来确定使用哪个变量。因此,除非您没有虚函数,否则编译器不需要创建虚函数表。 在我的机器上,虚函数表指针成员称为__vfptr。在调试时,除非添加虚函数,否则无法找到具有此名称的成员。这意味着您应该从计算中删除虚函数指针大小。 尝试运行以下代码并阅读注释以获取详细信息:

struct Empty
{

};

struct VirtualNotEmpty: public virtual Empty
{
    char c;
};

struct VirtualNotEmptyEx: public virtual Empty
{
    char c1;
    char c2;
    char c3;
    char c4;
};

struct TrueVirtualNotEmpty: public virtual Empty
{
    virtual void f() {}
    char c;
};

void inspect_vne()
{
    VirtualNotEmpty vne;

    char* start_of_struct = (char*)(&vne);
    char* end_of_struct = (char*)(&vne);
    end_of_struct += sizeof(vne);
    char* start_of_var = (char*)(&(vne.c));

    printf("sizeof(VirtualNotEmpty): %d\n", sizeof(vne));                    // 8 bytes
    printf("Address of struct %p\n", start_of_struct);
    printf("Lenght of struct %d\n", (int)(end_of_struct - start_of_struct));    // same as sizeof
    printf("Address of var c %p\n", start_of_var);
    printf("Diff from start: %d\n", (int)(start_of_var - start_of_struct));        // 4 bytes: 1 byte for struct and 3 bytes for padding
    printf("Diff from end: %d\n", (int)(end_of_struct - start_of_var));            // 4 bytes:    1 byte for struct and 3 bytes for padding

    // [st][pd][pd][pd] [ch][pd][pd][pd]
    // st -- struct vt -- virtual table, pd -- padding byte ch -- char member variable
}

void inspect_tvne()
{
    TrueVirtualNotEmpty vne;

    char* start_of_struct = (char*)(&vne);
    char* end_of_struct = (char*)(&vne);
    end_of_struct += sizeof(vne);
    char* start_of_var = (char*)(&(vne.c));

    printf("sizeof(TrueVirtualNotEmpty): %d\n", sizeof(vne));                //    12
    printf("Address of struct %p\n", start_of_struct);
    printf("Lenght of struct %d\n", (int)(end_of_struct - start_of_struct)); // same as sizeof
    printf("Address of var c %p\n", start_of_var);
    printf("Diff from start: %d\n", (int)(start_of_var - start_of_struct)); // 8 bytes: 1 byte for struct + 3 bytes of struct padding + 4 bytes o vptr
    printf("Diff from end: %d\n", (int)(end_of_struct - start_of_var));        // 4 bytes: 1 byte for member variable + 3 bytes for padding

    // [st][pd][pd][pd] [vt][vt][vt][vt] [ch][pd][pd][pd]
    // st -- struct vt -- virtual table, pd -- padding byte ch -- char member variable
    // member variable TrueVirtualNotEmpty::c can't be packed at first byte, because of order of dectaled members __vptr is declared bevore
    // TrueVirtualNotEmpty::c
}

int main(int, char**)
{
    printf("Size of pointer %d", sizeof(int*)); // 4 on my machine    
    inspect_vne();
    inspect_tvne();
    printf("Size of VirtualNotEmptyEx %d\n", sizeof(VirtualNotEmptyEx));    // there is no need to create padding at
                                                                            // end for c1-c4 chars because there is space to pack them
    return 0;
}

正如您所看到的,我的机器上的结构大小应该可以被4分割。