C++类内存分布

C++类内存分布

转自:https://www.cnblogs.com/jerry19880126/p/3616999.html
C++类内存分布

选择左侧的C/C+±>命令行,然后在其他选项这里写上/d1 reportAllClassLayout,它可以看到所有相关类的内存布局,如果写上/d1 reportSingleClassLayoutXXX(XXX为类名),则只会打出指定类XXX的内存布局。

多重继承

class Base
{
    int a;
    int b;
public:
    void CommonFunction() {};
    void virtual VirtualFunction() {};
};

class DerivedClass1 : public Base
{
    int c;
public:
    void DerivedCommonFunction() {};
    void virtual VirtualFunction() {};
};

class DerivedClass2 : public Base
{
    int d;
public:
    void DerivedCommonFunction() {};
    void virtual VirtualFunction() {};
};

class DerivedDerivedClass : public DerivedClass1, public DerivedClass2
{
    int e;
public:
    void DerivedDerivedCommonFunction() {};
    void virtual VirtualFunction() {};
};

C++类内存分布
C++类内存分布

下面我们重点看看这个类DerivedDerivedClass,由外向内看,它并列地排布着继承而来的两个父类DerivedClass1DerivedClass2,还有自身的成员变量e。DerivedClass1包含了它的成员变量c,以及BaseBase有一个0地址偏移的虚表指针,然后是成员变量abDerivedClass2的内存排布类似于DerivedClass1,注意到DerivedClass2里面竟然也有一份Base

  • DerivedDerivedClass1内存分布
1>class DerivedClass1    size(16):
1>  +---
1> 0    | +--- (base class Base)
1> 0    | | {vfptr}
1> 4    | | a
1> 8    | | b
1>    | +---
1>12    | c
1>  +---
1>
1>DerivedClass1::[email protected]:
1>  | &DerivedClass1_meta
1>  |  0
1> 0    | &DerivedClass1::VirtualFunction
1>
1>DerivedClass1::VirtualFunction this adjustor: 0
  • DerivedDerivedClass2内存分布
1>class DerivedClass2    size(16):
1>  +---
1> 0    | +--- (base class Base)
1> 0    | | {vfptr}
1> 4    | | a
1> 8    | | b
1>  | +---
1>12    | d
1>  +---
1>
1>DerivedClass2::[email protected]:
1>  | &DerivedClass2_meta
1>  |  0
1> 0    | &DerivedClass2::VirtualFunction
1>
1>DerivedClass2::VirtualFunction this adjustor: 0
1>
  • DerivedDerivedClass内存分布
1>class DerivedDerivedClass    size(36):
1>  +---
1> 0    | +--- (base class DerivedClass1)
1> 0    | | +--- (base class Base)
1> 0    | | | {vfptr}
1> 4    | | | a
1> 8    | | | b
1>  | | +---
1>12    | | c
1>  | +---
1>16    | +--- (base class DerivedClass2)
1>16    | | +--- (base class Base)
1>16    | | | {vfptr}
1>20    | | | a
1>24    | | | b
1>  | | +---
1>28    | | d
1>  | +---
1>32    | e
1>  +---
1>
1>DerivedDerivedClass::[email protected]@:
1>  | &DerivedDerivedClass_meta
1>  |  0
1> 0    | &DerivedDerivedClass::VirtualFunction
1>
1>DerivedDerivedClass::[email protected]@:
1>  | -16
1> 0    | &thunk: this-=16; goto DerivedDerivedClass::VirtualFunction
1>
1>DerivedDerivedClass::VirtualFunction this adjustor: 0

这里有两份虚表了,分别针对DerivedClass1DerivedClass2,在&DerivedDericedClass_meta下方的数字是首地址偏移量,靠下面的虚表的那个-16表示指向这个虚表的虚指针的内存偏移,这正是DerivedClass2中的{vfptr}DerivedDerivedClass的内存偏移。


多重继承使用虚继承

class Base
{
    int a;
    int b;
public:
    void CommonFunction() {};
    void virtual VirtualFunction() {};
};

class DerivedClass1 : public virtual  Base
{
    int c;
public:
    void DerivedCommonFunction() {};
    void virtual VirtualFunction() {};
};

class DerivedClass2 : virtual public Base
{
    int d;
public:
    void DerivedCommonFunction() {};
    void virtual VirtualFunction() {};
};

class DerivedDerivedClass : public DerivedClass1, public DerivedClass2
{
    int e;
public:
    void DerivedDerivedCommonFunction() {};
    void virtual VirtualFunction() {};
};
  • DerivedDerivedClass1内存分布
1>class DerivedClass1    size(20):
1>  +---
1> 0    | {vbptr}
1> 4    | c
1>  +---
1>    +--- (virtual base Base)
1> 8    | {vfptr}
1>12    | a
1>16    | b
1>  +---
1>
1>DerivedClass1::[email protected]:
1> 0    | 0
1> 1    | 8 (DerivedClass1d(DerivedClass1+0)Base)
1>
1>DerivedClass1::[email protected]:
1>  | -8
1> 0    | &DerivedClass1::VirtualFunction
1>
1>DerivedClass1::VirtualFunction this adjustor: 8
1>vbi:       class  offset o.vbptr  o.vbte fVtorDisp
1>            Base       8       0       4 0
1>

DerivedClass1就已经有变化了,原来是先排虚表指针与Base成员变量,vfptr位于0地址偏移处;但现在有两个虚表指针了,一个是vbptr,另一个是vfptrvbptr是这个DerivedClass1对应的虚表指针,它指向DerivedClass1的虚表vbtable,另一个vfptr是虚基类表对应的虚指针,它指向vftable

  • DerivedDerivedClass2内存分布
1>class DerivedClass2    size(20):
1>  +---
1> 0    | {vbptr}
1> 4    | d
1>  +---
1>  +--- (virtual base Base)
1> 8    | {vfptr}
1>12    | a
1>16    | b
1>  +---
1>
1>DerivedClass2::[email protected]:
1> 0    | 0
1> 1    | 8 (DerivedClass2d(DerivedClass2+0)Base)
1>
1>DerivedClass2::[email protected]:
1>  | -8
1> 0    | &DerivedClass2::VirtualFunction
1>
1>DerivedClass2::VirtualFunction this adjustor: 8
1>vbi:       class  offset o.vbptr  o.vbte fVtorDisp
1>            Base       8       0       4 0
1>
  • DerivedDerivedClass内存分布
1>class DerivedDerivedClass    size(32):
1>  +---
1> 0    | +--- (base class DerivedClass1)
1> 0    | | {vbptr}
1> 4    | | c
1>  | +---
1> 8    | +--- (base class DerivedClass2)
1> 8    | | {vbptr}
1>12    | | d
1>  | +---
1>16    | e
1>  +---
1>  +--- (virtual base Base)
1>20    | {vfptr}
1>24    | a
1>28    | b
1>  +---
1>
1>DerivedDerivedClass::[email protected]@:
1> 0    | 0
1> 1    | 20 (DerivedDerivedClassd(DerivedClass1+0)Base)
1>
1>DerivedDerivedClass::[email protected]@:
1> 0    | 0
1> 1    | 12 (DerivedDerivedClassd(DerivedClass2+0)Base)
1>
1>DerivedDerivedClass::[email protected]:
1>  | -20
1> 0    | &DerivedDerivedClass::VirtualFunction
1>
1>DerivedDerivedClass::VirtualFunction this adjustor: 20
1>vbi:       class  offset o.vbptr  o.vbte fVtorDisp
1>            Base      20       0       4 0

总结

虚继承的作用是减少了对基类的重复,代价是增加了虚表指针的负担(更多的虚表指针)。

下面总结一下(当基类有虚函数时):

  1. 每个类都有虚指针和虚表;

  2. 如果不是虚继承,那么子类将父类的虚指针继承下来,并指向自身的虚表(发生在对象构造时)。有多少个虚函数,虚表里面的项就会有多少。多重继承时,可能存在多个的基类虚表与虚指针;

  3. 如果是虚继承,那么子类会有两份虚指针,一份指向自己的虚表,另一份指向虚基表,多重继承时虚基表与虚基表指针有且只有一份。