C++类内存分布
C++类内存分布
转自:https://www.cnblogs.com/jerry19880126/p/3616999.html
选择左侧的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() {};
};
下面我们重点看看这个类DerivedDerivedClass
,由外向内看,它并列地排布着继承而来的两个父类DerivedClass1
与DerivedClass2
,还有自身的成员变量e。DerivedClass1
包含了它的成员变量c
,以及Base
,Base
有一个0地址偏移的虚表指针,然后是成员变量a
和b
;DerivedClass2
的内存排布类似于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
这里有两份虚表了,分别针对DerivedClass1
与DerivedClass2
,在&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
,另一个是vfptr
。vbptr
是这个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
总结
虚继承的作用是减少了对基类的重复,代价是增加了虚表指针的负担(更多的虚表指针)。
下面总结一下(当基类有虚函数时):
-
每个类都有虚指针和虚表;
-
如果不是虚继承,那么子类将父类的虚指针继承下来,并指向自身的虚表(发生在对象构造时)。有多少个虚函数,虚表里面的项就会有多少。多重继承时,可能存在多个的基类虚表与虚指针;
-
如果是虚继承,那么子类会有两份虚指针,一份指向自己的虚表,另一份指向虚基表,多重继承时虚基表与虚基表指针有且只有一份。