Это указатели смещения наверх (необходимы для множественного наследования) и указатели 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
и добавьте все дополнительные записи, которые нам понадобятся после этого. Функции, ожидающие B1
, будут счастливы, потому что они не будут использовать какую-либо часть виртуальной таблицы сверх того, что они ожидают для B1
.
Однако что произойдет, если D
сейчас также получено из B2
? Указатель на D
vtable не может быть и действительным B1
vtable и действительным B2
vtable! Компилятор решает эту проблему, добавляя отдельную B2
vtable в конец нашей объединенной D/B1
vtable, и корректирует указатель vtable вручную, когда мы пытаемся привести от D
к B2
.
Однако это приводит к новой проблеме - что происходит, когда мы пытаемся привести назад от B2
к D
? Компилятор не может просто отрегулировать указатель vtable в обратном направлении на ту же величину, в которой он корректировал указатель ранее, потому что он на самом деле не знает точно, что объект B2
, который мы ему передаем, имеет введите D
! В частности, dynamic_cast<D>()
должен быть в состоянии определить, относится ли наш объект к типу D
. Для этого ему необходимо получить доступ к RTTI объекта, а для , что , нужно знать, где находится начало vtable исходного объекта. Это цель значения смещения-на-вершине - оно дает нам смещение к началу vtable исходного объекта, мы получаем RTTI нашего объекта, а мстительный бог C ++ позволяет нашим культурам расти еще один сезон.
На этой странице есть несколько хороших примеров макетов vtable (в разделе Таблица 1c ). Обратите внимание, что они немного сложнее из-за использования виртуального наследования , которое добавляет дополнительное смещение к vtable каждого дочернего класса.