Какова первая запись (int (*) (...)) 0 vtable в выводе g ++ -fdump-class-иерархии? - PullRequest
48 голосов
/ 19 апреля 2011

Для этого кода:

class B1{
public:  
  virtual void f1() {}  
};

class D : public B1 {
public:
  void f1() {}
};

int main () {
    B1 *b1 = new B1();
    D  *d  = new D();

    return 0;
}

После компиляции виртуальная таблица, которую я получаю с g++ -fdump-class-hierarchy:

Vtable for B1
B1::_ZTV2B1: 3u entries
0     (int (*)(...))0
8     (int (*)(...))(& _ZTI2B1)
16    B1::f1


Vtable for D
D::_ZTV1D: 3u entries
0     (int (*)(...))0
8     (int (*)(...))(& _ZTI1D)
16    D::f1

Я не смог понять, что означают записи типа (int () (...)) 0 * соответствует.Конечно, это означает что-то вроде: это функция, которая возвращает int и принимает неограниченное количество аргументов, я ничего не понимаю дальше.Какой функции соответствует этот указатель функции?и откуда ты это знаешь?Мой 64-битный компьютер.

Указатель второй функции имеет адрес, связанный в конце ??Кому это соответствует?

РЕДАКТИРОВАТЬ

Я использую компилятор g ++:

g++ -v
Using built-in specs.
Target: x86_64-suse-linux
Configured with: ../configure --prefix=/usr --infodir=/usr/share/info --mandir=/usr/share/man --libdir=/usr/lib64 --libexecdir=/usr/lib64 --enable-languages=c,c++,objc,fortran,obj-c++,java,ada --enable-checking=release --with-gxx-include-dir=/usr/include/c++/4.4 --enable-ssp --disable-libssp --with-bugurl=http://bugs.opensuse.org/ --with-pkgversion='SUSE Linux' --disable-libgcj --disable-libmudflap --with-slibdir=/lib64 --with-system-zlib --enable-__cxa_atexit --enable-libstdcxx-allocator=new --disable-libstdcxx-pch --enable-version-specific-runtime-libs --program-suffix=-4.4 --enable-linux-futex --without-system-libunwind --with-arch-32=i586 --with-tune=generic --build=x86_64-suse-linux
Thread model: posix
*gcc version 4.4.1 [gcc-4_4-branch revision 150839] (SUSE Linux)*

Ответы [ 2 ]

50 голосов
/ 19 апреля 2011

Это указатели смещения наверх (необходимы для множественного наследования) и указатели 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 каждого дочернего класса.

3 голосов
/ 19 апреля 2011

Может быть, первая запись предназначена для виртуального деструктора, а вторая - для поддержки RTTI?Но это только предположение.

...