Виртуальные вызовы функций разрешаются во время выполнения, ссылаясь на номер VTable
экземпляра. Для любого виртуального класса существует отдельный VTable
(поэтому в каждом из A
, B
, C
и D
есть VTable
). Каждый экземпляр среды выполнения имеет указатель на одну из этих таблиц, определяемую его динамическим типом.
VTable
перечисляет каждую виртуальную функцию в классе, сопоставляя ее с фактической функцией, которая должна вызываться во время выполнения. Для нормального наследования они перечислены в порядке объявления, поэтому базовый класс может использовать VTable
производного класса для разрешения виртуальных функций, которые он объявляет (поскольку производный класс, перечисляющий функции в порядке описания, будет иметь все функции базового класса сначала перечислены в порядке убывания того же порядка, что и у базового класса VTable
). Для виртуального наследования (как указано выше) это немного сложнее, но по существу базовый класс внутри производного класса по-прежнему имеет свой собственный указатель VTable
, который указывает на соответствующий раздел внутри производного VTable
.
На практике это означает, что ваш класс D
имеет запись VTable
для g
, которая указывает на реализацию C
. Даже при доступе через статический тип B
он все равно будет ссылаться на этот же VTable
для разрешения g
.
Не делайте этого, это так называемый бриллиант смерти, см. Https://en.wikipedia.org/wiki/Multiple_inheritance – Bernhard
Я знаю, что это такое, я хочу знать, как это работает. –
Это может помочь, если вы объясните, почему вы думаете, что это не сработает. Не могли бы вы также смутить, если бы вы изменили первую строку 'main' на' A * b = new D; '? – Hurkyl