C++ Виртуальное наследование памяти
Макеты памяти виртуального наследования
Я пытаюсь полностью понять, что происходит под капотом в памяти с виртуальным наследованием и vTables / vPtrs, а что нет.
У меня есть два примера кода, который я написал, и я точно понимаю, почему они работают, однако я просто хочу убедиться, что у меня есть правильное представление о разметке памяти объектов.
Вот два примера на картинке, и я просто хочу знать, верны ли мои представления о разметке памяти.
Пример 1:
class Top { public: int a; };
class Left : public virtual Top { public: int b; };
class Right : public virtual Top { public: int c; };
class Bottom : public Left, public Right { public: int d; };
Пример 2:
То же, что и выше, но с:
class Right : public virtual Top {
public:
int c;
int a; // <======= added this
};
1 ответ
Стандарт C++ мало говорит о макете объекта. Виртуальные таблицы функций (vtables) и виртуальные базовые указатели даже не являются частью стандарта. Таким образом, вопрос и ответы могут только иллюстрировать возможные реализации.
Беглый взгляд на ваш рисунок, кажется, показывает правильное расположение.
Вас могут заинтересовать следующие ссылки:
Рассмотрено множественное наследование Полезная статья ddj о макете в случае множественного наследования и виртуального наследования.
Патент Microsoft, описывающий использование vfptr (таблицы виртуальных функций, aka vtables) и vbptr (виртуальные базовые poitners).
Изменить: наследует ли нижний Right::a
или же Left::a
?
В вашем тесте 2, Right
а также Left
иметь общего родителя Top
, Таким образом, есть только один подобъект Top
в Bottom
и, следовательно, существует только один и тот же Top::a
,
Интересно, что вы представили в своем тесте 2 участника a
в Right
, Это a
который отличается от a
унаследованный от Top
, Это отдельный член, и просто имеет то же имя, что и другая мембрана "по стечению обстоятельств". Так что, если вы получите доступ a
через Right
, Right::a
прячет Top::a
(что, кстати, также Bottom::Top::a
, Right::Top::a
, Left::Top::a
). В этом случае bottom.a означает Right::a не из-за расположения объекта, а из-за правил подстановки имен (и скрытия). И это не зависит от реализации: оно стандартно и переносимо.
Вот вариант для вашего теста 2, чтобы продемонстрировать ситуацию:
int main() {
Bottom bottom;
bottom.a = 7;
cout << bottom.Top::a << endl << bottom.Left::Top::a << endl;
cout << bottom.Right::Top::a << endl << bottom.Left::a << endl;
cout << bottom.Right::a << endl <<endl;
bottom.Right::a = 4;
bottom.Left::a = 3;
cout << bottom.Top::a << endl << bottom.Left::Top::a << endl;
cout << bottom.Right::Top::a << endl << bottom.Left::a << endl;
cout << bottom.Right::a << endl <<endl;
cout << "And the addresses are: " << endl;
cout << " Bottom: " << (void*)&bottom << endl;
cout << " Top: " << (void*)static_cast<Top*>(&bottom) << endl;
cout << " Left: " << (void*)static_cast<Left*>(&bottom) << endl;
cout << " Right: " << (void*)static_cast<Right*>(&bottom) << endl;
cout << " Top::a: " << (void*)&bottom.Top::a << endl;
cout << " Left::Top::a: " << (void*)&bottom.Left::Top::a << endl;
cout << " Right::Top::a:" << (void*)&bottom.Right::Top::a << endl;
cout << " Left::a: " << (void*)&bottom.Left::a << endl;
cout << " Rigth::a: " << (void*)&bottom.Right::a << endl;
};