考虑以下具有 3 级多重继承层次结构的代码。
auto addr = [](auto v) -> uint64_t { return *reinterpret_cast<uint64_t*>(v); };
struct BaseA
{
void virtual a() {}
};
struct BaseB
{
void virtual b() {}
};
struct BaseC : BaseA, BaseB
{
void virtual a() override {}
void virtual b() override {}
};
struct BaseD
{
void virtual d() {}
};
struct BaseE : BaseD, BaseC
{
void virtual d() override {}
void virtual a() override {}
void virtual b() override { auto a = this; std::cout << "called here: " << addr(&a) << "\n"; }
};
int main()
{
BaseE obj;
BaseE* ePtr = &obj;
BaseD* dPtr = &obj;
BaseC* cPtr = &obj;
BaseB* bPtr = &obj;
BaseA* aPtr = &obj;
ePtr->b();
cPtr->b();
bPtr->b();
std::cout << "e is at: " << addr(&ePtr) << "\n"
<< "d is at: " << addr(&dPtr) << "\n"
<< "a is at: " << addr(&aPtr) << "\n"
<< "c is at: " << addr(&cPtr) << "\n"
<< "b is at: " << addr(&bPtr) << "\n"
<< "total size is " << sizeof(BaseE) << "\n"
<< "vptr D and E " << vpt1 << "\n"
<< "vptr A and C " << vpt2 << "\n"
<< "vptr B " << vpt3 << "\n";
return 0;
}
此代码运行的输出如下:
called here: 140736308965696
called here: 140736308965696
called here: 140736308965696
e is at: 140736308965696
d is at: 140736308965696
a is at: 140736308965704
c is at: 140736308965704
b is at: 140736308965712
total size is 24
vptr D and E 4390608
vptr A and C 4390648
vptr B 4390680
这建议 BaseE obj 的以下内存布局(指针大小为 8 个字节)。
8 字节,BaseD 子对象,仅 vptr 到 D 表
8 字节,BaseA 子对象,vptr 只到 A 表
8 字节,BaseB 子对象,仅 vptr 到 B 表。
这里ePtr
和dPtr
都指向BaseD
子对象,aPtr
和都cPtr
指向子BaseA
对象和bPtr
子BaseB
对象。
我现在的问题是编译器将生成什么 thunk 代码来调整上述this
两个调用中的指针以b()
通过指针cPtr
并bPtr
确保this
正确指向ePtr
何时调用BaseE
的实现b()
?由于cPtr
并且bPtr
具有不同的地址,thunk 是否需要注意根据传递给它的基类指针的类型进行不同的调整?