Рассмотрим следующий код с 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 to 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, чтобы по-разному корректироваться в зависимости от того, какой тип указателя базового класса ему передан?