代码如下(在Ubuntu 16.04上用G++-5.4编译的C++11代码):
#include <iostream>
using namespace std;
class Base
{
public:
virtual void show()
{
cout << "Base" << endl;
}
virtual void func()
{
cout << "func in Base" << endl;
}
protected:
int base = 15;
};
class A: public virtual Base
{
public:
void show() override
{
cout << this << endl;
cout << 'A' << endl;
}
void func() override
{
cout << this << endl;
cout << "func in A" << endl;
}
protected:
int a = 31;
};
int main(int argc, const char *argv[])
{
A obj_a;
return 0;
}
我尝试使用 GDB 检查对象“obj_a”的内存布局(首先,我设置了“set print object on”、“set print pretty on”、“set print vtbl on”、“set print asm-demangle on "在 GDB 中):
(gdb) p sizeof(obj_a)
$1 = 32
(gdb) x/8aw &obj_a
0x7fffffffe320: 0x400d20 <vtable for A+24> 0x0 0x1f 0x0
0x7fffffffe330: 0x400d50 <vtable for A+72> 0x0 0xf 0x0
我们可以知道obj_a的开头和它的virtual base subobject之间的偏移量是16B(virtual base offset)。然后我检查 A 的由 0x400d08 (0x400d20 - 24) 指向的虚函数表:
(gdb) x/14ag 0x400d08
0x400d08 <vtable for A>: 0x10 0x0
0x400d18 <vtable for A+16>: 0x400d90 <typeinfo for A> 0x400b46 <A::show()>
0x400d28 <vtable for A+32>: 0x400b98 <A::func()> 0xfffffffffffffff0
0x400d38 <vtable for A+48>: 0xfffffffffffffff0 0xfffffffffffffff0
0x400d48 <vtable for A+64>: 0x400d90 <typeinfo for A> 0x400b8f <virtual thunk to A::show()>
0x400d58 <vtable for A+80>: 0x400be1 <virtual thunk to A::func()> 0x400d20 <vtable for A+24>
0x400d68 <VTT for A+8>: 0x400d50 <vtable for A+72> 0x0
正如我们所见,有两个“virtual thunk to xxx”,即“0x400b8f”和“0x400be1”。我查看了这两个地址。
(gdb) x/3i 0x400b8f
0x400b8f <virtual thunk to A::show()>: mov (%rdi),%r10
0x400b92 <virtual thunk to A::show()+3>: add -0x18(%r10),%rdi
0x400b96 <virtual thunk to A::show()+7>: jmp 0x400b46 <A::show()>
(gdb) x/3i 0x400be1
0x400be1 <virtual thunk to A::func()>: mov (%rdi),%r10
0x400be4 <virtual thunk to A::func()+3>: add -0x20(%r10),%rdi
0x400be8 <virtual thunk to A::func()+7>: jmp 0x400b98 <A::func()>
我的问题是:“add -0x18(%r10),%rdi”和“add -0x20(%r10),%rdi”到底是什么意思?为什么是 -24 (-0x18) 和 -32 (-0x20)? (我觉得应该都是-16)
最佳答案
谢谢 Rerito,对此感到抱歉。
我的问题是我对汇编代码不熟悉。
(gdb) x/3i 0x400b8f
0x400b8f <virtual thunk to A::show()>: mov (%rdi),%r10
0x400b92 <virtual thunk to A::show()+3>: add -0x18(%r10),%rdi
0x400b96 <virtual thunk to A::show()+7>: jmp 0x400b46 <A::show()>
在“virtual thunk to A::show()”的汇编代码中,%rdi 保存了“this”值。 "mov (%rdi),%r10"表示移动 "vptr"值(它的地址是 "this")到 r10 寄存器。 “add -0x18(%r10),%rdi”表示将地址为“vptr - 24”(即虚拟表中的0xffffffffffffffff0)的值添加到“this”。所以“这个”值可以更正为 A 的对象的地址。
关于c++ - 虚拟继承的虚拟函数表中的虚拟基址偏移量,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40627476/