1、怎么看到相关类的内存布局
设置VS,项目属性——命令行——其它选项:
在其他选项这里写上/d1 reportAllClassLayout,它可以看到所有相关类的内存布局
写上/d1 reportSingleClassLayoutXXX(XXX为类名),则只会打出指定类XXX的内存布局
2、运行
(1)普通基类
class Base {
int a;
int b;
static int c;
public:
void commonFunction();
};
输出内存分布:
1>class Base size(8):
1> +---
1> 0 | a
1> 4 | b
1> +---
可以看到:内存分布依据声明的顺序进行排列(类内偏移为0开始),成员函数不占内存空间,static成员变量分配在静态存储区域
(2)继承
class DerivedClass : public Base {
int c;
public:
void DerivedCommonFunction();
};
输出内存排布:
1>class DerivedClass size(12):
1> +---
1> 0 | +--- (base class Base)
1> 0 | | a
1> 4 | | b
1> | +---
1> 8 | c
1> +---
可以看到同样在内存排布上,先是排布了父类的成员变量,接着排布子类的成员变量
(3)基类加上虚函数
class Base {
int a;
int b;
public:
void commonFunction();
virtual void virtualFunction();
};
class DerivedClass : public Base {
int c;
public:
void DerivedCommonFunction();
};
内存排布:
1>class Base size(16):
1> +---
1> 0 | {vfptr}
1> 8 | a
1>12 | b
1> +---
1>
1>Base::$vftable@:
1> | &Base_meta
1> | 0
1> 0 | &Base::virtualFunction
1>
1>Base::virtualFunction this adjustor: 0
1>
1>class DerivedClass size(24):
1> +---
1> 0 | +--- (base class Base)
1> 0 | | {vfptr}
1> 8 | | a
1>12 | | b
1> | +---
1>16 | c
1> | <alignment member> (size=4)
1> +---
1>
1>DerivedClass::$vftable@:
1> | &DerivedClass_meta
1> | 0
1> 0 | &Base::virtualFunction
对于基类,看到把虚表指针放在了内存的开始处(0地址偏移),然后再是成员变量;下面生成了虚表,紧跟在&Base_meta后面的0表示,这张虚表对应的虚指针在内存中的分布 ;
对于子类,可以看到子类继承了基类的虚表指针,然后先排布了父类的成员变量,接着排布子类的成员变量;下面生成虚表,由于子类没有对虚函数进行重写,如果调用的是virtualFunction,那么会从父类中寻找对应的虚函数;
(4)在子类中添加虚函数(覆盖父类的虚方法并写一个新的虚函数)
class DerivedClass : public Base {
int c;
public:
void DerivedCommonFunction();
virtual void virtualFunction();
virtual void virtualFunction2();
};
1>class DerivedClass size(24):
1> +---
1> 0 | +--- (base class Base)
1> 0 | | {vfptr}
1> 8 | | a
1>12 | | b
1> | +---
1>16 | c
1> | <alignment member> (size=4)
1> +---
1>
1>DerivedClass::$vftable@:
1> | &DerivedClass_meta
1> | 0
1> 0 | &DerivedClass::virtualFunction
1> 1 | &DerivedClass::virtualFunction2
可以看出继承了父类的虚表指针,虚表的0号是子类的virtualFunction,而1号放的是子类的virtualFunction2
(5) 多重继承
class Base {
int a;
int b;
public:
void commonFunction();
virtual void virtualFunction();
};
class DerivedClass1 : public Base {
int c;
public:
void DerivedCommonFunction();
virtual void virtualFunction();
};
class DerivedClass2 : public Base {
int d;
public:
void DerivedCommonFunction();
virtual void virtualFunction();
};
class DerivedDerivedClass : public DerivedClass1, public DerivedClass2 {
int e;
public:
void DerivedCommonFunction();
virtual void virtualFunction();
};
父类排布:
1>class Base size(16):
1> +---
1> 0 | {vfptr}
1> 8 | a
1>12 | b
1> +---
1>
1>Base::$vftable@:
1> | &Base_meta
1> | 0
1> 0 | &Base::virtualFunction
1>
1>Base::virtualFunction this adjustor: 0
Base中有一个虚表指针,地址偏移为0
1>class DerivedClass1 size(24):
1> +---
1> 0 | +--- (base class Base)
1> 0 | | {vfptr}
1> 8 | | a
1>12 | | b
1> | +---
1>16 | c
1> | <alignment member> (size=4)
1> +---
1>
1>DerivedClass1::$vftable@:
1> | &DerivedClass1_meta
1> | 0
1> 0 | &DerivedClass1::virtualFunction
1>
1>DerivedClass1::virtualFunction this adjustor: 0
1>class DerivedClass2 size(24):
1> +---
1> 0 | +--- (base class Base)
1> 0 | | {vfptr}
1> 8 | | a
1>12 | | b
1> | +---
1>16 | d
1> | <alignment member> (size=4)
1> +---
1>
1>DerivedClass2::$vftable@:
1> | &DerivedClass2_meta
1> | 0
1> 0 | &DerivedClass2::virtualFunction
1>
1>DerivedClass2::virtualFunction this adjustor: 0
DerivedClass1和DerivedClass2继承了Base,内存排布是先父类后子类。
1>class DerivedDerivedClass size(56):
1> +---
1> 0 | +--- (base class DerivedClass1)
1> 0 | | +--- (base class Base)
1> 0 | | | {vfptr}
1> 8 | | | a
1>12 | | | b
1> | | +---
1>16 | | c
1> | | <alignment member> (size=4)
1> | +---
1>24 | +--- (base class DerivedClass2)
1>24 | | +--- (base class Base)
1>24 | | | {vfptr}
1>32 | | | a
1>36 | | | b
1> | | +---
1>40 | | d
1> | | <alignment member> (size=4)
1> | +---
1>48 | e
1> | <alignment member> (size=4)
1> +---
1>
1>DerivedDerivedClass::$vftable@DerivedClass1@:
1> | &DerivedDerivedClass_meta
1> | 0
1> 0 | &DerivedDerivedClass::virtualFunction
1>
1>DerivedDerivedClass::$vftable@DerivedClass2@:
1> | -24
1> 0 | &thunk: this-=24; goto DerivedDerivedClass::virtualFunction
1>
1>DerivedDerivedClass::virtualFunction this adjustor: 0
DerivedDerivedClass,由外向内看,它并列地排布着继承而来的两个父类DerivedClass1与DerivedClass2,还有自身的成员变量e。DerivedClass1包含了它的成员变量c,以及Base,Base有一个0地址偏移的虚表指针,然后是成员变量a和b;DerivedClass2的内存排布类似于DerivedClass1,注意到DerivedClass2里面竟然也有一份Base。
这里有两份虚表了,分别针对DerivedClass1与DerivedClass2,在&DerivedDericedClass_meta下方的数字是首地址偏移量,靠下面的虚表的那个-16表示指向这个虚表的虚指针的内存偏移,这正是DerivedClass2中的{vfptr}在DerivedDerivedClass的内存偏移。
(6)虚继承
class Base {
int a;
int b;
public:
void commonFunction();
virtual void virtualFunction();
};
class DerivedClass1 : virtual public Base {
int c;
public:
void DerivedCommonFunction();
virtual void virtualFunction();
};
class DerivedClass2 : virtual public Base {
int d;
public:
void DerivedCommonFunction();
virtual void virtualFunction();
};
class DerivedDerivedClass : public DerivedClass1, public DerivedClass2 {
int e;
public:
void DerivedCommonFunction();
virtual void virtualFunction();
};
1>class Base size(16):
1> +---
1> 0 | {vfptr}
1> 8 | a
1>12 | b
1> +---
1>
1>Base::$vftable@:
1> | &Base_meta
1> | 0
1> 0 | &Base::virtualFunction
1>
1>Base::virtualFunction this adjustor: 0
1>
1>class DerivedClass1 size(32):
1> +---
1> 0 | {vbptr}
1> 8 | c
1> | <alignment member> (size=4)
1> +---
1> +--- (virtual base Base)
1>16 | {vfptr}
1>24 | a
1>28 | b
1> +---
1>
1>DerivedClass1::$vbtable@:
1> 0 | 0
1> 1 | 16 (DerivedClass1d(DerivedClass1+0)Base)
1>
1>DerivedClass1::$vftable@:
1> | -16
1> 0 | &DerivedClass1::virtualFunction
1>
1>DerivedClass1::virtualFunction this adjustor: 16
1>vbi: class offset o.vbptr o.vbte fVtorDisp
1> Base 16 0 4 0
1>
1>class DerivedClass2 size(32):
1> +---
1> 0 | {vbptr}
1> 8 | d
1> | <alignment member> (size=4)
1> +---
1> +--- (virtual base Base)
1>16 | {vfptr}
1>24 | a
1>28 | b
1> +---
1>
1>DerivedClass2::$vbtable@:
1> 0 | 0
1> 1 | 16 (DerivedClass2d(DerivedClass2+0)Base)
1>
1>DerivedClass2::$vftable@:
1> | -16
1> 0 | &DerivedClass2::virtualFunction
1>
1>DerivedClass2::virtualFunction this adjustor: 16
1>vbi: class offset o.vbptr o.vbte fVtorDisp
1> Base 16 0 4 0
1>
1>class DerivedDerivedClass size(56):
1> +---
1> 0 | +--- (base class DerivedClass1)
1> 0 | | {vbptr}
1> 8 | | c
1> | | <alignment member> (size=4)
1> | +---
1>16 | +--- (base class DerivedClass2)
1>16 | | {vbptr}
1>24 | | d
1> | | <alignment member> (size=4)
1> | +---
1>32 | e
1> | <alignment member> (size=4)
1> +---
1> +--- (virtual base Base)
1>40 | {vfptr}
1>48 | a
1>52 | b
1> +---
1>
1>DerivedDerivedClass::$vbtable@DerivedClass1@:
1> 0 | 0
1> 1 | 40 (DerivedDerivedClassd(DerivedClass1+0)Base)
1>
1>DerivedDerivedClass::$vbtable@DerivedClass2@:
1> 0 | 0
1> 1 | 24 (DerivedDerivedClassd(DerivedClass2+0)Base)
1>
1>DerivedDerivedClass::$vftable@:
1> | -40
1> 0 | &DerivedDerivedClass::virtualFunction
1>
1>DerivedDerivedClass::virtualFunction this adjustor: 40
可以看到Base没有变化;
DerivedClass1有两个虚表指针了,一个是vbptr,另一个是vfptr。vbptr是这个DerivedClass1对应的虚表指针,它指向DerivedClass1的虚表vbtable,另一个vfptr是虚基类表对应的虚指针,它指向vftable。
DerivedClass2的内存分布类似于DerivedClass1,同样会有两个虚指针,分别指向两张虚表(第二张是虚基类表)。
DerivedDerivedClass的内存分布,这里面有三个虚指针了,但base却只有一份。
虚继承的作用是减少了对基类的重复,代价是增加了虚表指针的负担(更多的虚表指针)。
下面总结一下(当基类有虚函数时):
1. 每个类都有虚指针和虚表;
2. 如果不是虚继承,那么子类将父类的虚指针继承下来,并指向自身的虚表(发生在对象构造时)。有多少个虚函数,虚表里面的项就会有多少。多重继承时,可能存在多个的基类虚表与虚指针;
3. 如果是虚继承,那么子类会有两份虚指针,一份指向自己的虚表,另一份指向虚基表,多重继承时虚基表与虚基表指针有且只有一份。
参考:https://www.cnblogs.com/jerry19880126/p/3616999.html