自己做实验的时候发现和一些文章上面的有点不一样。
1 |
|
输出结果1
2
3
4
5
6
7
8
9
10
11offset:ffffffffffffffe8
[0] B1::_vptr->
[0] B1::f1()
[1] B1::Bf1()
[2] B1::ib1 = 1111111
[3] B1::cb1 = 1111
[3] B::_vptr->
[0] B::f()
[1] B::Bf()
[4] B::ib1 = 1010
[5] B::cb1 = 1010
GDB 调试 查看内存,有意思的来了。1
2
3
4
5
6
7
8(gdb) p pVtab
$1 = (long long **) 0x7ffdf9b52fc0 //一个B1对象的具体内存
(gdb) x /10xg 0x7ffdf9b52fc0
0x7ffdf9b52fc0: 0x0000562080dd6d08(这个是第一个虚表指针) 0x0000000001111111
0x7ffdf9b52fd0: 0x0000000000001111 0x0000562080dd6d38(第二个虚表指针)
0x7ffdf9b52fe0: 0x0000000000001010 0x0000000000001010
0x7ffdf9b52ff0: 0x0000562080bd61c0 0x0000562080bd617e
0x7ffdf9b53000: 0x00007ffdf9b52fc0 0x0000000200000000
有的文章里面说,虚拟继承每个基类会创建一个虚表,实际上并没有,只是多了一个虚表指针并没有那么多神奇的玩意。
你可以发现两个地址挨着非常近。我们查看 0x0000562080dd6d08
这个的内存。1
2
3
4
5
6gdb) x /10xg 0x0000562080dd6d08
0x562080dd6d08 <vtable for B1+24>: 0x0000562080bd6146 0x0000562080bd617e
0x562080dd6d18 <vtable for B1+40>: 0x0000000000000000 0x0000000000000000
0x562080dd6d28 <vtable for B1+56>: 0xffffffffffffffe8 0x0000562080dd6d78
0x562080dd6d38 <vtable for B1+72>: 0x0000562080bd607e(第二个虚表指针在这???) 0x0000562080bd60b6
0x562080dd6d48 <VTT for B1>: 0x0000562080dd6d08(有没有发现这个地址就是第一个虚表指针,并没有NULL,所以我那个地方写的是i<2) 0x0000562080dd6d38
看到了吗,实际上全都在B1的虚表里面,只是指针指向的位置不一样。还有个更有意思的。
有没有发现有两个0x0000000000000000
目前不知道是啥。0xffffffffffffffe8
这个刚好就是偏移量,并没有什么指针直接指向他。所以我不知道虚基表指针是啥,也没看到一个指针算进对象的空间。
然后再继续看这两个虚表指针的内容。1
2
3
4
5
6
7
8
9
10
11
12
13(gdb) x /10xg 0x0000562080bd6146 //不出意外是个函数
0x562080bd6146 <B1::f1()>: 0x10ec8348e5894855 0xfb358d48f87d8948
0x562080bd6156 <B1::f1()+16>: 0x0f003d8d48000000 0x48fffff97be80020
0x562080bd6166 <B1::f1()+32>: 0x200e89058b48c289 0xe8d78948c6894800
0x562080bd6176 <B1::f1()+48>: 0x90c3c990fffff976 0x10ec8348e5894855
0x562080bd6186 <B1::Bf1()+8>: 0xcc358d48f87d8948 0x0ec83d8d48000000
(gdb) x /10xg 0x0000562080bd607e //第二个指针也是
0x562080bd607e <B::f()>: 0x10ec8348e5894855 0xb4358d48f87d8948
0x562080bd608e <B::f()+16>: 0x0fc83d8d48000001 0x48fffffa43e80020
0x562080bd609e <B::f()+32>: 0x200f51058b48c289 0xe8d78948c6894800
0x562080bd60ae <B::f()+48>: 0x90c3c990fffffa3e 0x10ec8348e5894855
0x562080bd60be <B::Bf()+8>: 0x83358d48f87d8948 0x0f903d8d48000001
然后就是更有意思的。记得我们原本注释的吗,我们把他取消注释再来GDB调试。1
2
3(gdb) x /10xg 0x000055aec160519d //因为重新运行了地址不一样,我们像那样看第二个虚指针第一项的值
0x55aec160519d <virtual thunk to B1::f()>: 0xebe87a0349178b4c 0xec8348e5894855c0
//看到了 virtual thunk ,这个告诉我们这个已经被重写了,然后又会调用B1::f()
是不是发现这个布局很像这种写法。1
2
3class B{
A a;
}
当然只是有点像,到了菱形继承就差距很大了。我们继续探索。
1 |
|
运行结果1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22D size:56
B1 size:32
D address:0x7fff13a26610
B1 address:0x7fff13a26610
B1-B2 offset:-16
B2-B offset:-24
B address:0x7fff13a26638
[0] B1::_vptr->
[0] D::f()
[1] D::f1()
[2] B1::Bf1()
[3] D::f2()
[4] D::Df()
[1] B1::ib1 = 1111
[2] B2::_vptr->
[0] D::f2()
[1] B2::Bf2()
[3] B2::ib2 = 2222
[4] B::ib = dddd
[5] B::_vptr->
[1] B::Bf()
[6] B::ib = bbbbb
GDB查看内存。1
2
3
4
5
6(gdb) x /10xg pVtab
0x7ffc64cadc10: 0x000055c32e665b88(B1的虚指针) 0x0000000000001111
0x7ffc64cadc20: 0x000055c32e665bc8(B2的虚指针) 0x0000000000002222
0x7ffc64cadc30: 0x000000000000dddd(D的数据) 0x000055c32e665bf8(B的虚指针)
0x7ffc64cadc40: 0x00000000000bbbbb 0x0000000000000000
0x7ffc64cadc50: 0x0000000000000000 0x00007ffc64cadc10
看到这应该都清楚了。显然B布局到了最后面。大概就是因为这个所以才能实现只有一份拷贝吧。具体详细分析,自己进去看吧,就是把东西结合起来。
为了方便查看偏移量 我GDB 调试 10进制1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26(gdb) x /10g pVtab
0x7ffd2687ea70: 94162329979784 4369
0x7ffd2687ea80: 94162329979848 8738
0x7ffd2687ea90: 56797 94162329979896
0x7ffd2687eaa0: 768955 0
0x7ffd2687eab0: 0 140725249895024
(gdb) x /10dg 94162329979784
0x55a3e03acb88 <vtable for D+24>: 94162327877818 94162327877882
0x55a3e03acb98 <vtable for D+40>: 94162327877436 94162327877938
0x55a3e03acba8 <vtable for D+56>: 94162327878000 24(不知道具体怎么排列的,但是知道有就行了)
0x55a3e03acbb8 <vtable for D+72>: -16 94162329980184
0x55a3e03acbc8 <vtable for D+88>: 94162327877993 94162327877624
(gdb) x /10dg 94162329979848
0x55a3e03acbc8 <vtable for D+88>: 94162327877993 94162327877624
0x55a3e03acbd8 <vtable for D+104>: 0 -40
0x55a3e03acbe8 <vtable for D+120>: -40 94162329980184
0x55a3e03acbf8 <vtable for D+136>: 94162327877873 94162327877184
0x55a3e03acc08 <VTT for D>: 94162329979784 94162329979992
(gdb) x /10dg 94162329979896
0x55a3e03acbf8 <vtable for D+136>: 94162327877873 94162327877184
0x55a3e03acc08 <VTT for D>: 94162329979784 94162329979992
0x55a3e03acc18 <VTT for D+16>: 94162329980048 94162329980088
0x55a3e03acc28 <VTT for D+32>: 94162329980136 94162329979896
0x55a3e03acc38 <VTT for D+48>: 94162329979848 40
看了好多博客,都搞不懂,自己做出来的和他们有点不一样。
结论你们自己得吧,告辞。