Ответ 1
Это указатели смещения к вершине (необходимы для множественного наследования) и указатели typeinfo (RTTI).
Из Itanium ABI (вы не используете компилятор Itanium, но их описание действительно хорошее):
Смещение к вершине содержит смещение к вершине объекта от местоположения в пределах объекта указателя виртуальной таблицы, который обращается к этой виртуальной таблице, как ptrdiff_t. Это всегда присутствует. Смещение позволяет найти верхнюю часть объекта из любого базового подобъекта с помощью указателя виртуальной таблицы. Это необходимо, в частности, для dynamic_cast.
(В полной виртуальной таблице объекта и, следовательно, во всех ее основных базовых виртуальных таблицах значение этого смещения будет равно нулю. [...])Указатель typeinfo указывает на объект typeinfo, используемый для RTTI. Это всегда присутствует. Все записи в каждой из виртуальных таблиц для данного класса должны указывать на один и тот же объект typeinfo. Корректная реализация равенства typeinfo заключается в проверке равенства указателей, за исключением указателей (прямо или косвенно) на неполные типы. Указатель typeinfo является допустимым указателем для полиморфных классов, то есть классов с виртуальными функциями, и равен нулю для неполиморфных классов.
Смещение сверху вверх более подробно (по запросу)
Допустим, у вас есть производный класс D
, производный от базового класса B1
. Что происходит, когда вы пытаетесь привести экземпляр D
к типу B1
? Поскольку функция, которые принимают B1
объект ничего не знает о D
, часть D
виртуальных таблиц также должна быть действительными B1
виртуальными таблицами. Это достаточно просто - просто сделайте старт D
таблицы похожим на B1
Vtable и добавьте все дополнительные записи, которые нам понадобятся после этого. Функции, ожидающие B1
будут счастливы, потому что они не будут использовать какую-либо часть vtable помимо того, что они ожидают для B1
.
Однако что произойдет, если D
теперь также получен из B2
? Указатель на D
виртуальных таблицы не может быть как действительная B1
виртуальных таблиц и действительный B2
виртуальных таблицами! Компилятор решает эту проблему, добавляя отдельную B2
таблицу B2
в конец нашей объединенной D/B1
таблицы D/B1
, и вручную корректирует указатель виртуальной таблицы, когда мы пытаемся преобразовать D
в B2
.
Тем не менее, это приводит к новой проблеме - что происходит, когда мы пытаемся вернуться из B2
в D
? Компилятор не может просто настроить указатель vtable в обратном направлении на ту же величину, в которой он корректировал указатель ранее, потому что он на самом деле не знает наверняка, что объект B2
мы ему передаем, имеет тип D
! В частности, dynamic_cast<D>()
должен иметь возможность определить, относится ли наш объект к типу D
или нет. Для этого ему необходим доступ к объекту RTTI, и для этого ему нужно знать, где находится исходный объект vtable. Это цель значения смещения к вершине - оно дает нам смещение к началу исходного объекта vtable, мы получаем наш объект RTTI, а мстительный бог C++ позволяет нашим культурам расти в течение другого сезона.
На этой странице есть несколько хороших примеров макетов vtable (в Таблице 1c). Обратите внимание, что они немного сложнее из-за использования виртуального наследования, которое добавляет дополнительное смещение к vtable каждого дочернего класса.