一、编译环境
- Visual Studio 2017
- G++ (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0
- Clang++ 6.0.0-1ubuntu2
二、非虚继承
2.1 测试代码
// filename: nonvirtual.cpp
#include <cstdio>
struct A
{
virtual void FA() {}
long long a = 0x1111111111111111LL;
};
struct B : A
{
virtual void FB() {}
long long b = 0x2222222222222222LL;
};
struct C : A
{
virtual void FC() {}
long long c = 0x3333333333333333LL;
};
struct D : B, C
{
virtual void FD() {}
long long d = 0x4444444444444444LL;
};
int main(int argc, char** argv)
{
A a;
B b;
C c;
D d;
printf("sizeof(void*) = %zu\n", sizeof(void*));
printf("sizeof(a) = %zu, &a = %p\n", sizeof(a), &a);
printf("sizeof(b) = %zu, &b = %p\n", sizeof(b), &b);
printf("sizeof(c) = %zu, &c = %p\n", sizeof(c), &c);
printf("sizeof(d) = %zu, &d = %p\n", sizeof(d), &d);
return 0;
}
2.2 编译方式
2.2.1 VS20177
Debug x64
2.2.2 G++
g++ -std=c++11 -ggdb -o gcc-nv.out nonvirtual.cpp
2.2.3 Clang++
clang++ -std=c++11 -ggdb -o clang-nv.out nonvirtual.cpp
2.3 执行结果
2.3.1 VS2017
执行结果
sizeof(void*) = 8 sizeof(a) = 16, &a = 000000204193F818 sizeof(b) = 24, &b = 000000204193F848 sizeof(c) = 24, &c = 000000204193F878 sizeof(d) = 56, &d = 000000204193F8A8
类D的内存布局
+----------------+ | | | +------------+ | | | | | +---------+ | | +--------+ | | | | | | | A.vptr+---------->+ A::FA() | | | | A::a | | | | B::FB() | | | +--------+ | | | D::FD() | | | B::b | | | | | +------------+ | +---------+ | | | +------------+ | | | | | +---------+ | | +--------+ | | | | | | | A.vptr+---------->+ A::FA() | | | | A::a | | | | C::FC() | | | +--------+ | | | | | | C::c | | +---------+ | +------------+ | | | | D::d | | | +----------------+
类D的内存DUMP
0x000000204193F8A8 00007ff7c032b1e8 1111111111111111 2222222222222222 00007ff7c032ae18 1111111111111111 3333333333333333 0x000000204193F8D8 4444444444444444 0x00007FF7C032B1E8 00007ff7c0321302 00007ff7c03211b8 00007ff7c032105a 0x00007FF7C032AE18 00007ff7c0321302 00007ff7c03212e4
2.3.2 G++
执行结果
sizeof(void*) = 8 sizeof(a) = 16, &a = 0x7fffffffe350 sizeof(b) = 24, &b = 0x7fffffffe360 sizeof(c) = 24, &c = 0x7fffffffe380 sizeof(d) = 56, &d = 0x7fffffffe3a0
类D的内存布局
和VS2017的结果类似。
类D的内存DUMP
(gdb) x /7a &d 0x7fffffffe3a0: 0x555555755ca8 <_ZTV1D+16> 0x1111111111111111 0x7fffffffe3b0: 0x2222222222222222 0x555555755cd0 <_ZTV1D+56> 0x7fffffffe3c0: 0x1111111111111111 0x3333333333333333 0x7fffffffe3d0: 0x4444444444444444 (gdb) x /3a 0x555555755ca8 0x555555755ca8 <_ZTV1D+16>: 0x555555554b96 <A::FA()> 0x555555554ba2 <B::FB()> 0x555555755cb8 <_ZTV1D+32>: 0x555555554bba <D::FD()> (gdb) x /2a 0x555555755cd0 0x555555755cd0 <_ZTV1D+56>: 0x555555554b96 <A::FA()> 0x555555554bae <C::FC()>
2.3.3 Clang++
执行结果
sizeof(void*) = 8 sizeof(a) = 16, &a = 0x7fffffffe3c0 sizeof(b) = 24, &b = 0x7fffffffe3a8 sizeof(c) = 24, &c = 0x7fffffffe390 sizeof(d) = 56, &d = 0x7fffffffe358
类D的内存布局
和VS2017的结果类似。
类D的内存DUMP
(gdb) x /7a &d 0x7fffffffe358: 0x400a88 <_ZTV1D+16> 0x1111111111111111 0x7fffffffe368: 0x2222222222222222 0x400ab0 <_ZTV1D+56> 0x7fffffffe378: 0x1111111111111111 0x3333333333333333 0x7fffffffe388: 0x4444444444444444 (gdb) x /3a 0x400a88 0x400a88 <_ZTV1D+16>: 0x400880 <A::FA()> 0x400890 <B::FB()> 0x400a98 <_ZTV1D+32>: 0x4008b0 <D::FD()> (gdb) x /2a 0x400ab0 0x400ab0 <_ZTV1D+56>: 0x400880 <A::FA()> 0x4008a0 <C::FC()>
三、虚继承
3.1 测试代码
与非虚拟继承的测试代码类似,只是将非虚拟继承改成了虚拟继承。
// filename: virtual.cpp
#include <cstdio>
struct A
{
virtual void FA() {}
long long a = 0x1111111111111111LL;
};
struct B : virtual public A
{
virtual void FB() {}
long long b = 0x2222222222222222LL;
};
struct C : virtual public A
{
virtual void FC() {}
long long c = 0x3333333333333333LL;
};
struct D : B, C
{
virtual void FD() {}
long long d = 0x4444444444444444LL;
};
int main(int argc, char** argv)
{
A a;
B b;
C c;
D d;
auto pba = dynamic_cast<A*>(&b);
auto pda = dynamic_cast<A*>(&d);
auto pdb = dynamic_cast<B*>(&d);
printf("sizeof(void*) = %zu\n", sizeof(void*));
printf("sizeof(a) = %zu, &a = %p\n", sizeof(a), &a);
printf("sizeof(b) = %zu, &b = %p\n", sizeof(b), &b);
printf("sizeof(c) = %zu, &c = %p\n", sizeof(c), &c);
printf("sizeof(d) = %zu, &d = %p\n", sizeof(d), &d);
return 0;
}
3.2 编译方式
3.2.1 vs2017
Debug x64
3.2.2 G++
g++ -std=c++11 -ggdb -o gcc-v.out virtual.cpp
3.2.3 Clang++
clang++ -std=c++11 -ggdb -o clang-v.out virtual.cpp
3.3 执行结果
VS2017的执行结果与G++和Clang++不同。
3.3.1 VS2017
执行结果
sizeof(void*) = 8 sizeof(a) = 16, &a = 00000002F65FF658 sizeof(b) = 40, &b = 00000002F65FF688 sizeof(c) = 40, &c = 00000002F65FF6C8 sizeof(d) = 72, &d = 00000002F65FF710
对类B的分析
类B的内存分布
+--------------+ | | +---------+ | +----------+ | | B::FB() +<--------+B.vptr | | +--------------------+ +---------+ | | B.offset+-------->+ offset2ptr = -8 | | | B::b | | | offset2A.vptr = 16 | | +----------+ | +--------------------+ | | +---------+ | +----------+ | | A::FA() +<-------+A.vptr | | +---------+ | | A::a | | | +----------+ | | | +--------------+
类B的内存DUMP
0x00000002F65FF688 00007ff6cd4eadb0 00007ff6cd4eb1e0 2222222222222222 00007ff6cd4eadc8 1111111111111111 0x00007FF6CD4EADB0 00007ff6cd4e11b8 <B::FB()> 0x00007FF6CD4EB1E0 fffffff8 00000010 0x00007FF6CD4EADC8 00007ff6cd4e1302 <A::FA()>
对类D的分析
类D的内存布局
offset中,offset2ptr的数据是没使用的。
+--------------+ | | +---------+ | +----------+ | | B::FB() +<-------+B.vptr | | +--------------------+ | D::FD() | | | B.offset+-------->+ offset2ptr = -8 | +---------+ | | B::b | | | offset2A.vptr = 48 | | +----------+ | | offset2vptr = -8 | | | | offset2C.vptr = 24 | +---------+ | +----------+ | +--------------------+ | C::FC() +<-------+C.vptr | | +--------------------+ +---------+ | | C.offset+-------->+ offset2vptr = -8 | | | C::c | | | offset2A.vptr = 24 | | +----------+ | +--------------------+ | | | D::d | | | +---------+ | +----------+ | | A::FA() +<-------+A.vptr | | +---------+ | | A::a | | | +----------+ | | | +--------------+
类D的内存DUMP
0x00000002F65FF710 00007ff6cd4eb098 00007ff6cd4eac10 2222222222222222 00007ff6cd4eae70 00007ff6cd4eac18 3333333333333333 0x00000002F65FF740 4444444444444444 00007ff6cd4eae90 1111111111111111 0x00007FF6CD4EB098 00007ff6cd4e11b8 <B::FB()> 00007ff6cd4e105a <D::FD()> 0x00007FF6CD4EAC10 fffffff8 00000030 fffffff8 00000018 0x00007FF6CD4EAE70 00007ff6cd4e12e4 <C::FC()> 0x00007FF6CD4EAC18 fffffff8 00000018 0x00007FF6CD4EAE90 00007ff6cd4e1302 <A::FA()>
3.3.2 G++
执行结果
sizeof(void*) = 8 sizeof(a) = 16, &a = 0x7fffffffe360 sizeof(b) = 32, &b = 0x7fffffffe370 sizeof(c) = 32, &c = 0x7fffffffe390 sizeof(d) = 56, &d = 0x7fffffffe3b0
对类B的分析
类B的内存DUMP
类B的内存布局
+------------+ | | | +--------+ | +---------+ | | B.vptr+------->+ B::FB() | | | B::b | | +---------+ | +--------+ | | +--------| | +---------+ | | A.vptr+------->+ A::FA() | | | A::a | | +---------+ | +--------+ | | | +------------+
>>> x /4a &b 0x7fffffffe370: 0x555555755cc8 <_ZTV1B+24> 0x2222222222222222 0x7fffffffe380: 0x555555755ce8 <_ZTV1B+56> 0x1111111111111111 >>> x /1a 0x555555755cc8 0x555555755cc8 <_ZTV1B+24>: 0x555555554dc4 <B::FB()> >>> x /1a 0x555555755ce8 0x555555755ce8 <_ZTV1B+56>: 0x555555554db8 <A::FA()>
对类D的分析
类D的内存布局
+----------------+ | | | +------------+ | | | | | | | +--------+ | | +---------+ | | | B.vptr+---------->+ B::FB() | | | | B::b | | | | D::FD() | | | +--------+ | | +---------+ | | +--------| | | +---------+ | | | C.vptr+---------->+ C::FC() | | | | C::c | | | +---------+ | | +--------+ | | | | | | | +------------+ | | | | D::d | | | | +--------+ | +---------+ | | A.vptr+---------->+ A::FA() | | | A::a | | +---------+ | +--------+ | | | +----------------+
类D的内存DUMP
>>> x /7a &d 0x7fffffffe3b0: 0x555555755b58 <_ZTV1D+24> 0x2222222222222222 0x7fffffffe3c0: 0x555555755b80 <_ZTV1D+64> 0x3333333333333333 0x7fffffffe3d0: 0x4444444444444444 0x555555755ba0 <_ZTV1D+96> 0x7fffffffe3e0: 0x1111111111111111 >>> x /2a 0x555555755b58 0x555555755b58 <_ZTV1D+24>: 0x555555554dc4 <B::FB()> 0x555555554ddc <D::FD()> >>> x /1a 0x555555755b80 0x555555755b80 <_ZTV1D+64>: 0x555555554dd0 <C::FC()> >>> x /1a 0x555555755ba0 0x555555755ba0 <_ZTV1D+96>: 0x555555554db8 <A::FA()>
3.3.3 Clang++
Clang++的结果和G++的一样。
执行结果
sizeof(void*) = 8 sizeof(a) = 16, &a = 0x7fffffffe3c0 sizeof(b) = 32, &b = 0x7fffffffe3a0 sizeof(c) = 32, &c = 0x7fffffffe380 sizeof(d) = 56, &d = 0x7fffffffe348
对类B的分析
类B的内存布局
+------------+ | | | +--------+ | +---------+ | | B.vptr+------->+ B::FB() | | | B::b | | +---------+ | +--------+ | | +--------| | +---------+ | | A.vptr+------->+ A::FA() | | | A::a | | +---------+ | +--------+ | | | +------------+
类B的内存DUMP
>>> x /4a &b 0x7fffffffe3a0: 0x400b60 <_ZTV1B+24> 0x2222222222222222 0x7fffffffe3b0: 0x400b80 <_ZTV1B+56> 0x1111111111111111 >>> x /1a 0x400b60 0x400b60 <_ZTV1B+24>: 0x400960 <B::FB()> >>> x /1a 0x400b80 0x400b80 <_ZTV1B+56>: 0x400950 <A::FA()>
对类D的分析
类D的内存布局
+----------------+ | | | +------------+ | | | | | | | +--------+ | | +---------+ | | | B.vptr+---------->+ B::FB() | | | | B::b | | | | D::FD() | | | +--------+ | | +---------+ | | +--------| | | +---------+ | | | C.vptr+---------->+ C::FC() | | | | C::c | | | +---------+ | | +--------+ | | | | | | | +------------+ | | | | D::d | | | | +--------+ | +---------+ | | A.vptr+---------->+ A::FA() | | | A::a | | +---------+ | +--------+ | | | +----------------+
类D的内存DUMP
>>> x /7a &d 0x7fffffffe348: 0x400c60 <_ZTV1D+24> 0x2222222222222222 0x7fffffffe358: 0x400c88 <_ZTV1D+64> 0x3333333333333333 0x7fffffffe368: 0x4444444444444444 0x400ca8 <_ZTV1D+96> 0x7fffffffe378: 0x1111111111111111 >>> x /2a 0x400c60 0x400c60 <_ZTV1D+24>: 0x400960 <B::FB()> 0x400a00 <D::FD()> >>> x /1a 0x400c88 0x400c88 <_ZTV1D+64>: 0x400970 <C::FC()> >>> x /1a 0x400ca8 0x400ca8 <_ZTV1D+96>: 0x400950 <A::FA()>
四、分析结论
4.1 结论1
菱形非虚继承时 VS2017 、 G++ 7.5 和 Clang++ 6 编译生成的类的内存布局相同。
+----------------+
| |
| +------------+ |
| | | | +---------+
| | +--------+ | | | |
| | | A.vptr+---------->+ A::FA() |
| | | A::a | | | | B::FB() |
| | +--------+ | | | D::FD() |
| | B::b | | | |
| +------------+ | +---------+
| |
| +------------+ |
| | | | +---------+
| | +--------+ | | | |
| | | A.vptr+---------->+ A::FA() |
| | | A::a | | | | C::FC() |
| | +--------+ | | | |
| | C::c | | +---------+
| +------------+ |
| |
| D::d |
| |
+----------------+
4.2 结论2
菱形虚拟继承时 VS2017 编译生成类的内存布局与 G++ 7.5 和 Clang++ 6 的结果不同。
VS2017生成的类的内存布局:
+--------------+ | | +---------+ | +----------+ | | B::FB() +<--------+B.vptr | | +--------------------+ | D::FD() | | | B.offset+-------->+ offset2ptr = -8 | +---------+ | | B::b | | | offset2A.vptr = 48 | | +----------+ | | offset2vptr = -8 | | | | offset2C.vptr = 24 | +---------+ | +----------+ | +--------------------+ | C::FC() +<-------+C.vptr | | +--------------------+ +---------+ | | C.offset+-------->+ offset2vptr = -8 | | | C::c | | | offset2A.vptr = 24 | | +----------+ | +--------------------+ | | | D::d | | | +---------+ | +----------+ | | A::FA() +<-------+A.vptr | | +---------+ | | A::a | | | +----------+ | | | +--------------+
G++ 7.5 和 Clang++ 6 生成的类的内存布局相同:
+----------------+ | | | +------------+ | | | | | | | +--------+ | | +---------+ | | | B.vptr+---------->+ B::FB() | | | | B::b | | | | D::FD() | | | +--------+ | | +---------+ | | +--------| | | +---------+ | | | C.vptr+---------->+ C::FC() | | | | C::c | | | +---------+ | | +--------+ | | | | | | | +------------+ | | | | D::d | | | | +--------+ | +---------+ | | A.vptr+---------->+ A::FA() | | | A::a | | +---------+ | +--------+ | | | +----------------+