Я не уверен на 100%, что этот ответ правильный, но вот мое лучшее предположение.
Когда у вас есть класс, который наследует умножается и не виртуально, макет класса обычно является полнымобъект первого базового типа, затем полный объект второго базового типа, затем данные для самого объекта.Если вы посмотрите на B, вы увидите указатель vtable для объекта A, а если вы посмотрите на C, то увидите, что в vtable есть указатели для объектов A и B.
Поскольку объектытаким образом, это означает, что если у вас есть указатель B*
, указывающий на объект C
, указатель фактически не будет находиться у основания объекта;скорее он будет указывать где-то посередине.Это означает, что если вам когда-либо понадобится привести объект к A*
, вам нужно отрегулировать указатель B*
на некоторое количество, чтобы пропустить его обратно к началу объекта.Чтобы сделать это, компилятору нужно где-то кодировать количество байтов, которое нужно пропустить назад, чтобы добраться до начала объекта.Я думаю, что самый первый (int(*)(...))
- это просто необработанное число байтов, на которое нужно посмотреть, чтобы добраться до начала объекта.Если вы заметите, что для A
vtable этот указатель равен 0 (поскольку vtable для A находится в начале объекта, и то же самое верно для B
vtable (так как он также живет в началеобъект. Однако обратите внимание, что таблица C
состоит из двух частей - первая часть является таблицей для A
, и ее первая сумасшедшая запись также равна нулю (поскольку, если вы находитесь в таблице A
, выне нужно вносить никаких корректировок). Однако после первой половины этой таблицы есть то, что выглядит как B
vtable, и обратите внимание, что его первой записью является шестнадцатеричное значение -0x10
.* при компоновке объекта вы заметите, что указатель vtable B
составляет 16 байт после указателя vtable * 1019. * Это значение -0x10
может быть корректирующим смещением, которое необходимо пропустить через указатель vtable B
, чтобы получитьназад к корню объекта.
Вторая сумасшедшая запись каждой vtable кажется указателем на сам vtable. Обратите внимание, что он всегда равен адресу объекта vtable (сравнитеназвание виртуальной таблицы и на что она указывает).Это было бы необходимо, если бы вы хотели выполнить какую-либо идентификацию типа среды выполнения, поскольку обычно для этого требуется посмотреть на адрес виртуальной таблицы (или, по крайней мере, на что-то в передней части).есть зашифрованные функции setInt и getInt в конце таблицы C
, я уверен, что это потому, что тип C
наследует два разных набора функций с именами setInt
и getInt
- от одного до A
и от одного до B
.Если бы мне пришлось угадывать, проблема заключается в том, чтобы внутренние компоненты компилятора могли различать две виртуальные функции.
Надеюсь, это поможет!