使用虚方法的C++对象大小

sky*_*oor 26 c++ virtual-functions objectsize

我对虚拟对象大小有一些疑问.

1)虚函数

class A {
    public:
       int a;
       virtual void v();
    }
Run Code Online (Sandbox Code Playgroud)

A类的大小是8字节....一个整数(4个字节)加上一个虚拟指针(4个字节)很清楚!

class B: public A{
    public:
       int b;
       virtual void w();
}
Run Code Online (Sandbox Code Playgroud)

B级的大小是多少?我使用sizeof B测试,它打印12

这是否意味着只有一个vptr,即使B类和A类都有虚函数?为什么只有一个vptr?

class A {
public:
    int a;
    virtual void v();
};

class B {
public:
    int b;
    virtual void w();
};

class C :  public A, public B {
public:
    int c;
    virtual void x();
};
Run Code Online (Sandbox Code Playgroud)

C的大小是20 ........

似乎在这种情况下,两个vptrs在布局中......这是怎么发生的?我认为两个vptrs一个用于A类,另一个用于B类....所以没有vptr用于C类的虚函数?

我的问题是,关于继承中vptrs数量的规则是什么?

2)虚拟继承

    class A {
    public:
        int a;
        virtual void v();
    };

    class B: virtual public A{                  //virtual inheritance 
    public:
        int b;
        virtual void w();
    };

    class C :  public A {                      //non-virtual inheritance
    public:
        int c;
        virtual void x();
    };

class D: public B, public C {
public:
    int d;
    virtual void y();
};
Run Code Online (Sandbox Code Playgroud)

A的大小是8个字节-------------- 4(int a)+ 4(vptr)= 8

B的大小是16个字节--------------没有虚拟它应该是4 + 4 + 4 = 12.为什么这里还有4个字节?B级的布局是什么?

C的大小是12个字节.-------------- 4 + 4 + 4 = 12.很清楚!

D的大小是32个字节--------------它应该是16(B类)+ 12(C类)+ 4(int d)= 32.是吗?

    class A {
    public:
        int a;
        virtual void v();
    };

    class B: virtual public A{                       //virtual inheritance here
    public:
        int b;
        virtual void w();
    };

    class C :  virtual public A {                    //virtual inheritance here
    public:
        int c;
        virtual void x();
    };

  class D: public B, public C {
   public:
        int d;
        virtual void y();
    };
Run Code Online (Sandbox Code Playgroud)

sizeof A是8

sizeof B是16

sizeof C是16

sizeof D是28是表示28 = 16(B类)+ 16(C类) - 8(A类)+ 4(这是什么?)

我的问题是,为什么在应用虚拟继承时会有额外的空间?

在这种情况下,对象大小的下面规则是什么?

虚拟应用于所有基类和部分基类时有什么区别?

Ter*_*fey 21

这是所有实现定义的.我正在使用VC10 Beta2.有助于理解这些东西的关键(虚函数的实现),您需要了解Visual Studio编译器中的秘密开关/ d1reportSingleClassLayoutXXX.我会在一秒钟内完成.

基本规则是对于任何指向对象的指针,vtable需要位于偏移量0处.这意味着多个vtable用于多重继承.

在这里结合问题,我将从顶部开始:

这是否意味着只有一个vptr,即使B类和A类都有虚函数?为什么只有一个vptr?

这就是虚函数的工作方式,您希望基类和派生类共享相同的vtable指针(指向派生类中的实现).

似乎在这种情况下,两个vptrs在布局中......这是怎么发生的?我认为两个vptrs一个用于A类,另一个用于B类....所以没有vptr用于C类的虚函数?

这是C类的布局,由/ d1reportSingleClassLayoutC报告:

class C size(20):
        +---
        | +--- (base class A)
 0      | | {vfptr}
 4      | | a
        | +---
        | +--- (base class B)
 8      | | {vfptr}
12      | | b
        | +---
16      | c
        +---
Run Code Online (Sandbox Code Playgroud)

你是对的,有两个vtable,每个基类一个.这是它在多重继承中的工作方式; 如果C*被转换为B*,指针值将被调整8个字节.vtable仍然需要在偏移0处才能使虚函数调用起作用.

上述A类布局中的vtable被视为C类的vtable(当通过C*调用时).

B的大小是16个字节--------------没有虚拟它应该是4 + 4 + 4 = 12.为什么这里还有4个字节?B级的布局是什么?

这是此示例中B类的布局:

class B size(20):
        +---
 0      | {vfptr}
 4      | {vbptr}
 8      | b
        +---
        +--- (virtual base A)
12      | {vfptr}
16      | a
        +---
Run Code Online (Sandbox Code Playgroud)

如您所见,有一个额外的指针来处理虚拟继承.虚拟继承很复杂.

D的大小是32个字节--------------它应该是16(B类)+ 12(C类)+ 4(int d)= 32.是吗?

不,36个字节.同样处理虚拟继承.本例中D的布局:

class D size(36):
        +---
        | +--- (base class B)
 0      | | {vfptr}
 4      | | {vbptr}
 8      | | b
        | +---
        | +--- (base class C)
        | | +--- (base class A)
12      | | | {vfptr}
16      | | | a
        | | +---
20      | | c
        | +---
24      | d
        +---
        +--- (virtual base A)
28      | {vfptr}
32      | a
        +---
Run Code Online (Sandbox Code Playgroud)

我的问题是,为什么在应用虚拟继承时会有额外的空间?

虚基类指针,它很复杂.基类在虚拟继承中"组合".该类不是将基类嵌入到类中,而是具有指向布局中基类对象的指针.如果您有两个使用虚拟继承的基类("菱形"类层次结构),它们将指向对象中的相同虚拟基类,而不是具有该基类的单独副本.

在这种情况下,对象大小的下面规则是什么?

很重要的一点; 没有规则:编译器可以做任何需要做的事情.

最后的细节; 制作我正在编译的所有这些类布局图:

cl test.cpp /d1reportSingleClassLayoutXXX
Run Code Online (Sandbox Code Playgroud)

其中XXX是结构/类的子字符串匹配,您希望看到它的布局.使用此方法,您可以自己探索各种继承方案的影响,以及添加填充的原因/位置等.