Почему тебя это волнует? Простой ответ: достаточно , но я думаю, вы хотите что-то более полное.
Это не является частью стандарта, поэтому любая реализация может делать все, что пожелает, но общее практическое правило заключается в том, что в реализации, использующей указатели виртуальных таблиц, в качестве нулевого приближения, для динамической диспетчеризации, которая вам нужна максимум столько указателей на виртуальные таблицы, сколько существует классов, которые добавляют новый виртуальный метод в иерархию. (В некоторых случаях виртуальная таблица может быть расширена, а базовый и производный типы совместно используют один vptr
)
// some examples:
struct a { void foo(); }; // no need for virtual table
struct b : a { virtual foo1(); }; // need vtable, and vptr
struct c : b { void bar(); }; // no extra virtual table, 1 vptr (b) suffices
struct d : b { virtual bar(); }; // extra vtable, need b.vptr and d.vptr
struct e : d, b {}; // 3 vptr, 2 for the d subobject and one for
// the additional b
struct f : virtual b {};
struct g : virtual b {};
struct h : f, g {}; // single vptr, only b needs vtable and
// there is a single b
По сути, каждому подобъекту типа, который требует своей собственной динамической диспетчеризации (не может напрямую повторно использовать родительские элементы), потребуется собственная виртуальная таблица и vptr.
В действительности компиляторы объединяют разные таблицы в одну таблицу. Когда d
добавляет новую виртуальную функцию поверх набора функций в b
, компилятор объединит потенциальные две таблицы в одну, добавив новые слоты в конец vtable, поэтому vtable для d
будет расширенной версией vtable для b
с дополнительными элементами в конце, поддерживающими двоичную совместимость (т. е. vtable d
можно интерпретировать как v 1015 * vtable для доступа к методам, доступным в b
), а d
объект будет иметь один vptr
.
В случае множественного наследования все становится немного сложнее, поскольку каждая база должна иметь ту же компоновку, что и подобъект завершенного объекта, чем если бы это был отдельный объект, поэтому будут дополнительные vptr, указывающие на разные области в полная таблица объекта.
Наконец, в случае виртуального наследования вещи становятся еще более сложными, и может быть несколько vtables для одного и того же завершенного объекта с обновлением vptr по мере развития строительства / разрушения (vptr всегда обновляется по мере развития строительства / разрушения, но без Виртуальное наследование vptr будет указывать на vtables базы, тогда как в случае виртуального наследования будет несколько vtables для одного и того же типа)