C++ 类继承的对象布局

C++多重继承下,对象布局与编译器,是否为虚拟继承都有很大关系,下面将逐一分析其中的差别,相同点为都按照类继承的先后顺序布局(类内按照虚表、成员声明先后顺序排列)。该类情况为子类按照继承顺序排列,如class C:public B,public A此种情况布局如下:

C++ 类继承的对象布局

如果class B,A带有虚函数,情况又发生了变化:

考虑下面的情况:

C++ 类继承的对象布局

class D {
int d;
public:
virtual void fun() {}
virtual ~D() {}
}; class E {
public:
virtual void fun() {}
virtual ~E() {}
private:
int e;
}; class F:public D,public E {
public:
void fun() {D.fun();}
virtual ~F() {}
private:
int f;
};

类F函数的布局中无虚表(vs2010,20个字节):

class F	size(20):
+---
| +--- (base class D)
0 | | {vfptr}
4 | | d
| +---
| +--- (base class E)
8 | | {vfptr}
12 | | e
| +---
16 | f
+---

g++ 编译器中包含虚表:

Class F    	size=20 align=4   	base size=20 base align=4    F (0x2ea1740) 0
vptr=((& F::_ZTV1F) + 8u)
D (0x2e76b28) 0
primary-for F (0x2ea1740)
E (0x2e76b60) 8
vptr=((& F::_ZTV1F) + 28u)

2)  当出现基类重复继承情况

如非虚拟继承,重复继承,编译器会产生告警信息:

warning: direct base 'D' inaccessible in 'G' due to ambiguity [enabled by default]

C++ 类继承的对象布局

编译器产生的布局与情况1)相同,都是按照继承的顺序排列

3) 虚拟继承(非菱形继承)

基类若非包含继承同一个父类,虚拟继承实际不存在,考虑下面的继承体系:

C++ 类继承的对象布局

类F继承自类D,类G虚拟继承D,F,类的布局图如下(与情况1相同):

g++ -fdump-class-hierarchy -c multiInherit.cpp
Class H
size=32 align=4
base size=4 base align=4
H (0x2372b80) 0 nearly-empty
vptridx=0u vptr=((& H::_ZTV1H) + 16u)
D (0x231b9d8) 4 virtual
vptridx=4u vbaseoffset=-12 vptr=((& H::_ZTV1H) + 40u)
F (0x2372bc0) 12 virtual
vptridx=8u vbaseoffset=-16 vptr=((& H::_ZTV1H) + 68u)
D (0x231ba10) 12
primary-for F (0x2372bc0) E (0x231ba48) 20 vptridx=12u vptr=((& H::_ZTV1H) + 88u)

vs2010 编译布局与cl -d1reportAllClassLayout multiInherit.cpp,与类G布局不同,类H这里产生了虚表:

class H	size(32):
+---
0 | {vbptr}
+---
+--- (virtual base D)
4 | {vfptr}
8 | d
+---
+--- (virtual base F)
| +--- (base class D)
12 | | {vfptr}
16 | | d
| +---
| +--- (base class E)
20 | | {vfptr}
24 | | e
| +---
28 | f
+---

4) 虚拟继承(菱形继承)

菱形继承情况如下

C++ 类继承的对象布局

无论类K是否虚拟自I,J,g++产生的类布局都相似(基类都产生虚表,不过地址指向同一内存):

g++ -fdump-class-hierarchy -c multiInherit.cpp
Class K
size=16 align=4
base size=8 base align=4
K (0x237f340) 0
vptridx=0u vptr=((& K::_ZTV1K) + 12u)
I (0x237f380) 0 nearly-empty
primary-for K (0x237f340)
subvttidx=4u
D (0x231bc40) 8 virtual
vptridx=20u vbaseoffset=-12 vptr=((& K::_ZTV1K) + 56u)
J (0x237f3c0) 4 nearly-empty
subvttidx=12u vptridx=24u vptr=((& K::_ZTV1K) + 32u)
D (0x231bc40) alternative-path

类L的布局如下:

Class L
size=16 align=4
base size=4 base align=4
L (0x237f680) 0 nearly-empty
vptridx=0u vptr=((& L::_ZTV1L) + 24u)
I (0x237f6c0) 0 nearly-empty virtual
primary-for L (0x237f680)
subvttidx=16u vptridx=4u vbaseoffset=-20
D (0x231bd58) 4 virtual
vptridx=8u vbaseoffset=-12 vptr=((& L::_ZTV1L) + 48u)
J (0x237f700) 12 nearly-empty virtual
subvttidx=24u vptridx=12u vbaseoffset=-24 vptr=((& L::_ZTV1L) + 76u)
D (0x231bd58) alternative-path

VS 2010产生的类布局与是否虚拟继承有很大关系,未虚拟继承将不产生虚表且相同基类排列在子类下面而非其子类的父类下:

cl -d1reportAllClassLayout multiInherit.cpp
class K size(16):
+---
| +--- (base class I)
0 | | {vbptr}
| +---
| +--- (base class J)
4 | | {vbptr}
| +---
+---
+--- (virtual base D)
8 | {vfptr}
12 | d
+---

类L的布局产生了虚表,且重新排列了基类顺序:

class L	size(20):
+---
0 | {vbptr}
+---
+--- (virtual base D)
4 | {vfptr}
8 | d
+---
+--- (virtual base I)
12 | {vbptr}
+---
+--- (virtual base J)
16 | {vbptr}
+---
上一篇:(摘)C#生成随机数的三种方法


下一篇:StackExchange.Redis学习笔记(五) 发布和订阅