VC、GCC及Clang中C++虚继承和虚函数的内存布局

测试代码如下:

1#include <stdio.h>
2
3class A
4{
5public:
6    int a;
7    virtual ~A(void){}
8};
9class B : virtual public A
10{
11};
12class C : virtual public A
13{
14    virtual void FuncC(void) {}
15};
16class D : public B, public C
17{
18};
19class E : public B
20{
21    virtual void FuncE(void) {}
22};
23
24int main(int argc, char** argv)
25{
26    A a;
27    B b;
28    C c;
29    D d;
30    E e;
31    printf("%d %d %d %d %d\n",
32        sizeof(A), sizeof(B), sizeof(C), sizeof(D), sizeof(E));
33
34    return 0;
35}

1、VC

VC根据是否为虚继承来判断是否在继承关系中共享虚函数表指针。使用下面的命令即可查看源文件main.cpp中类X的内存分布。结果中vfptr为虚函数表指针,vbptr为虚基类指针。(cl版本信息为Microsoft (R) C/C++ Optimizing Compiler Version 19.00.23026 for x86)

1cl main.cpp /d1reportSingleClassLayoutX
(1)A


1class A size(8):
2        +---
3 0      | {vfptr}
4 4      | a
5        +---

(2)B
 

1class B size(12):
2        +---
3 0      | {vbptr}
4        +---
5        +--- (virtual base A)
6 4      | {vfptr}
7 8      | a
8        +---

(3)C。和B对比我们可以看出,只有子类有新的虚函数时,编译器才会在子类中添加虚表指针。

1class C size(16):
2        +---
3 0      | {vfptr}
4 4      | {vbptr}
5        +---
6        +--- (virtual base A)
7 8      | {vfptr}
812      | a
9        +---

(4)D

1class D size(20):
2        +---
3        | +--- (base class C)
4 0      | | {vfptr}
5 4      | | {vbptr}
6        | +---
7        | +--- (base class B)
8 8      | | {vbptr}
9        | +---
10        +---
11        +--- (virtual base A)
1212      | {vfptr}
1316      | a
14        +---

(5)E

1class E size(16):
2        +---
3 0      | {vfptr}
4        | +--- (base class B)
5 4      | | {vbptr}
6        | +---
7        +---
8        +--- (virtual base A)
9 8      | {vfptr}
1012      | a
11        +---

2、GCC

GCC中虚继承的子类和父类是共享虚函数指针的。

运行下面的命令可以查看源文件main.cpp中所有类的内存分布情况。运行命令后会生成一个名为main.cpp.002t.class。这个文件中包了类的内存分布,虚表内容等信息。注意,需要较高版本的gcc。(g++版本为g++ (GCC) 4.8.5 20150623 (Red Hat 4.8.5-4),系统为CentOS 7.2.1511 x86_64)

1g++ -fdump-class-hierarchy main.cpp
2cat main.cpp.002t.class
(1)A


1Class A
2   size=16 align=8
3   base size=12 base align=8
4A (0x0x7fc56a101960) 0
5    vptr=((& A::_ZTV1A) + 16u)

(2)B

1Class B
2   size=24 align=8
3   base size=8 base align=8
4B (0x0x7fc56a0f6138) 0 nearly-empty
5    vptridx=0u vptr=((& B::_ZTV1B) + 24u)
6  A (0x0x7fc56a1019c0) 8 virtual
7      vptridx=8u vbaseoffset=-24 vptr=((& B::_ZTV1B) + 64u)

(3)C

1Class C
2   size=24 align=8
3   base size=8 base align=8
4C (0x0x7fc56a0f62d8) 0 nearly-empty
5    vptridx=0u vptr=((& C::_ZTV1C) + 24u)
6  A (0x0x7fc56a101a20) 8 virtual
7      vptridx=8u vbaseoffset=-24 vptr=((& C::_ZTV1C) + 72u)

(4)D

1Class D
2   size=32 align=8
3   base size=16 base align=8
4D (0x0x7fc56a1339a0) 0
5    vptridx=0u vptr=((& D::_ZTV1D) + 24u)
6  B (0x0x7fc56a0f63a8) 0 nearly-empty
7      primary-for D (0x0x7fc56a1339a0)
8      subvttidx=8u
9    A (0x0x7fc56a101a80) 16 virtual
10        vptridx=40u vbaseoffset=-24 vptr=((& D::_ZTV1D) + 112u)
11  C (0x0x7fc56a0f6410) 8 nearly-empty
12      subvttidx=24u vptridx=48u vptr=((& D::_ZTV1D) + 64u)
13    A (0x0x7fc56a101a80) alternative-path

(5)E

1Class E
2   size=24 align=8
3   base size=8 base align=8
4E (0x0x7fc56a0f6680) 0 nearly-empty
5    vptridx=0u vptr=((& E::_ZTV1E) + 24u)
6  B (0x0x7fc56a0f66e8) 0 nearly-empty
7      primary-for E (0x0x7fc56a0f6680)
8      subvttidx=8u
9    A (0x0x7fc56a101ae0) 8 virtual
10        vptridx=24u vbaseoffset=-24 vptr=((& E::_ZTV1E) + 72u)

3、Clang

Clang中虚继承的子类和父类是共享虚函数指针的。

Clang可以使用下面的命令查看main.cpp文件中所有结构的内存分布。结果在stdout中。(Clang版本为clang version 3.4.2 (tags/RELEASE_34/dot2-final)Target: x86_64-redhat-linux-gnu,系统为CentOS 7.2.1511 x86_64)

1clang -Xclang -fdump-record-layouts

(1)A

1*** Dumping AST Record Layout
2   0 | class A
3   0 |   (A vtable pointer)
4   8 |   int a
5     | [sizeof=16, dsize=12, align=8
6     |  nvsize=12, nvalign=8]

(2)B

1*** Dumping AST Record Layout
2   0 | class B
3   0 |   (B vtable pointer)
4   8 |   class A (virtual base)
5   8 |     (A vtable pointer)
6  16 |     int a
7     | [sizeof=24, dsize=20, align=8
8     |  nvsize=8, nvalign=8]

(3)C

1*** Dumping AST Record Layout
2   0 | class C
3   0 |   (C vtable pointer)
4   8 |   class A (virtual base)
5   8 |     (A vtable pointer)
6  16 |     int a
7     | [sizeof=24, dsize=20, align=8
8     |  nvsize=8, nvalign=8]

(4)D

1*** Dumping AST Record Layout
2   0 | class D
3   0 |   class B (primary base)
4   0 |     (B vtable pointer)
5   8 |   class C (base)
6   8 |     (C vtable pointer)
7  16 |   class A (virtual base)
8  16 |     (A vtable pointer)
9  24 |     int a
10     | [sizeof=32, dsize=28, align=8
11     |  nvsize=16, nvalign=8]

(5)E

1*** Dumping AST Record Layout
2   0 | class E
3   0 |   class B (primary base)
4   0 |     (B vtable pointer)
5   8 |   class A (virtual base)
6   8 |     (A vtable pointer)
7  16 |     int a
8     | [sizeof=24, dsize=20, align=8
9     |  nvsize=8, nvalign=8]

结论:

1、虚基类指针

VC、GCC和Clang这三个编译器的实现中,不管是否是虚继承还是有虚函数,其虚基类指针都不共享,都是单独的。

2、虚函数表指针

(1)VC根据是否为虚继承来判断是否在继承关系中共享虚表指针。如果子类是虚继承拥有虚函数父类,且子类有新加的虚函数时,子类中则会新加一个虚函数表指针。

(2)GCC和Clang的虚表指针在整个继承关系中共享的。

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值