Что такое VTT для класса? - PullRequest
28 голосов
/ 07 июня 2011

Недавно столкнулся с новой ошибкой компоновщика C ++.

libfoo.so: undefined reference to `VTT for Foo'
libfoo.so: undefined reference to `vtable for Foo'

Я распознал ошибку и исправил свою проблему, но у меня все еще есть вопрос: что такое VTT?

В стороне: Для тех, кто заинтересован, проблема возникает, когда вы забыли определить первую виртуальную функцию, объявленную в классе. Vtable входит в модуль компиляции первой виртуальной функции класса. Если вы забудете определить эту функцию, вы получите ошибку компоновщика, из-за которой он не может найти vtable, а гораздо более дружественный для разработчика не может найти функцию.

Ответы [ 6 ]

41 голосов
/ 19 апреля 2013

Страница "Замечания о множественном наследовании в компиляторе GCC C ++ v4.0.1" сейчас не в сети, и http://web.archive.org не архивировал его.Итак, я нашел копию текста в tinydrblog , которая заархивирована в веб-архиве .

Полный текст оригинальных примечаний опубликован в Интернете какчасть семинара Моргана Детерса «« Докторантура по языковому программированию: GCC Internals »» (осень 2005 г.) в лаборатории вычислительных систем распределенных объектов факультета компьютерных наук Вашингтонского университета в Сент-Луисе. »
Его (заархивированная) домашняя страница :

THIS IS THE TEXT by Morgan Deters and NOT CC-licensed.

Веб-страницы Моргана Детерса:

PART1:

Основы: одиночное наследование

Как мы уже говорили в классе, одиночное наследование приводит к компоновке объекта с данными базового класса, расположенными перед данными производного класса.Таким образом, если классы A и B определены следующим образом:

class A {
public:
  int a;

};

class B : public A {
public:
  int b;
};

, тогда объекты типа B размещаются следующим образом (где "b"это указатель на такой объект):

b --> +-----------+
      |     a     |
      +-----------+
      |     b     |
      +-----------+

Если у вас есть виртуальные методы:

class A {
public:
  int a;
  virtual void v();
};

class B : public A {
public:
  int b;
};

, то у вас также будет указатель vtable:

                           +-----------------------+
                           |     0 (top_offset)    |
                           +-----------------------+
b --> +----------+         | ptr to typeinfo for B |
      |  vtable  |-------> +-----------------------+
      +----------+         |         A::v()        |
      |     a    |         +-----------------------+
      +----------+
      |     b    |
      +----------+

, то есть top_offset и указатель typeinfo располагаются выше местоположения, на которое указывает указатель vtable.

Простое множественное наследование

Теперь рассмотрим множественное наследование:

class A {
public:
  int a;
  virtual void v();
};

class B {
public:
  int b;
  virtual void w();
};

class C : public A, public B {
public:
  int c;
};

В этом случае объекты типа C размещаются следующим образом:

                           +-----------------------+
                           |     0 (top_offset)    |
                           +-----------------------+
c --> +----------+         | ptr to typeinfo for C |
      |  vtable  |-------> +-----------------------+
      +----------+         |         A::v()        |
      |     a    |         +-----------------------+
      +----------+         |    -8 (top_offset)    |
      |  vtable  |---+     +-----------------------+
      +----------+   |     | ptr to typeinfo for C |
      |     b    |   +---> +-----------------------+
      +----------+         |         B::w()        |
      |     c    |         +-----------------------+
      +----------+

... но почему?Почему два vtables в одном?Ну, подумай о замене типов.Если у меня есть указатель на C, я могу передать его функции, которая ожидает указатель на A, или функции, которая ожидает указатель на B.Если функция ожидает указатель на A, и я хочу передать ей значение моей переменной c (типа pointer-to-C), я уже установлен.Вызовы A::v() могут быть сделаны через (первую) vtable, и вызываемая функция может получить доступ к члену a через указатель, который я передаю, так же, как и через любой указатель на A.

Однако, если я передам значение моей переменной-указателя c функции, которая ожидает указатель на B, нам также понадобится подобъект типа B в нашем C, чтобы ссылаться на него.Вот почему у нас есть второй указатель vtable.Мы можем передать значение указателя (c + 8 байт) в функцию, которая ожидает указатель на B, и все готово: она может делать вызовы B::w() через (второй) указатель vtable и получать доступ к члену bчерез указатель мы проходим так же, как и через любой указатель на B.

Обратите внимание, что это "исправление указателя" должно происходить и для вызываемых методов.Класс C наследует B::w() в этом случае.Когда w() вызывается через указатель на C, указатель (который становится указателем this внутри w() необходимо настроить. Это часто называют корректировкой указателя.

В некоторых случаяхкомпилятор сгенерирует thunk для исправления адреса. Рассмотрим тот же код, что и выше, но на этот раз C переопределяет B функцию члена w():

class A {
public:
  int a;
  virtual void v();
};

class B {
public:
  int b;
  virtual void w();
};

class C : public A, public B {
public:
  int c;
  void w();
};

C 'Схема объекта s и vtable теперь выглядят так:

                           +-----------------------+
                           |     0 (top_offset)    |
                           +-----------------------+
c --> +----------+         | ptr to typeinfo for C |
      |  vtable  |-------> +-----------------------+
      +----------+         |         A::v()        |
      |     a    |         +-----------------------+
      +----------+         |         C::w()        |
      |  vtable  |---+     +-----------------------+
      +----------+   |     |    -8 (top_offset)    |
      |     b    |   |     +-----------------------+
      +----------+   |     | ptr to typeinfo for C |
      |     c    |   +---> +-----------------------+
      +----------+         |    thunk to C::w()    |
                           +-----------------------+

Теперь, когда w() вызывается для экземпляра C через указатель на B, вызывается thunk.сделаем? Давайте разберем его (здесь, с gdb):

0x0804860c <_ZThn8_N1C1wEv+0>:  addl   $0xfffffff8,0x4(%esp)
0x08048611 <_ZThn8_N1C1wEv+5>:  jmp    0x804853c <_ZN1C1wEv>

Так что он просто настраивает указатель this и переходит на C::w(). Все хорошо.

Но не означает ли это, что vtable B всегда указывает на этот C::w() thunk?Я имею в виду, если у нас есть указатель на B, который на законных основаниях равен B (а не C), мы не хотим вызывать thunk, верно?

Верно.Вышеупомянутый встроенный vtable для B в C является особенным для случая B-in-C.Обычный vtable класса B является нормальным и напрямую указывает на B::w().

The Diamond: несколько копий базовых классов (не виртуальное наследование)

Хорошо.Теперь заняться действительно сложными вещами.Вспомните обычную проблему множественных копий базовых классов при формировании ромба наследования:

class A {
public:
  int a;
  virtual void v();
};

class B : public A {
public:
  int b;
  virtual void w();
};

class C : public A {
public:
  int c;
  virtual void x();
};

class D : public B, public C {
public:
  int d;
  virtual void y();
};

Обратите внимание, что D наследуется как от B и C, так и от B и C обоихнаследовать от A.Это означает, что D содержит две копии A.Расположение объектов и встраивание vtable - это то, что мы ожидали от предыдущих разделов:

                           +-----------------------+
                           |     0 (top_offset)    |
                           +-----------------------+
d --> +----------+         | ptr to typeinfo for D |
      |  vtable  |-------> +-----------------------+
      +----------+         |         A::v()        |
      |     a    |         +-----------------------+
      +----------+         |         B::w()        |
      |     b    |         +-----------------------+
      +----------+         |         D::y()        |
      |  vtable  |---+     +-----------------------+
      +----------+   |     |   -12 (top_offset)    |
      |     a    |   |     +-----------------------+
      +----------+   |     | ptr to typeinfo for D |
      |     c    |   +---> +-----------------------+
      +----------+         |         A::v()        |
      |     d    |         +-----------------------+
      +----------+         |         C::x()        |
                           +-----------------------+

Конечно, мы ожидаем, что данные A (член a) будут существовать дважды в Dмакет объекта (и он есть), и мы ожидаем, что виртуальные функции-члены A будут представлены дважды в vtable (и A::v() действительно есть).Ладно, ничего нового здесь.

The Diamond: одиночные копии виртуальных баз

Но что, если мы применим виртуальное наследование?Виртуальное наследование C ++ позволяет нам определять алмазную иерархию, но гарантируется только одна копия виртуально унаследованных баз.Итак, давайте напишем наш код следующим образом:

class A {
public:
  int a;
  virtual void v();
};

class B : public virtual A {
public:
  int b;
  virtual void w();
};

class C : public virtual A {
public:
  int c;
  virtual void x();
};

class D : public B, public C {
public:
  int d;
  virtual void y();
};

Внезапно все становится намного сложнее.Если у нас может быть только одна копия A в нашем представлении D, то мы больше не сможем избавиться от нашей «хитрости» встраивания C в D (и встраивания vtable для C часть D в D vtable).Но как мы можем справиться с обычной заменой типов, если мы не можем этого сделать?

Давайте попробуем построить схему:

                                   +-----------------------+
                                   |   20 (vbase_offset)   |
                                   +-----------------------+
                                   |     0 (top_offset)    |
                                   +-----------------------+
                                   | ptr to typeinfo for D |
                      +----------> +-----------------------+
d --> +----------+    |            |         B::w()        |
      |  vtable  |----+            +-----------------------+
      +----------+                 |         D::y()        |
      |     b    |                 +-----------------------+
      +----------+                 |   12 (vbase_offset)   |
      |  vtable  |---------+       +-----------------------+
      +----------+         |       |    -8 (top_offset)    |
      |     c    |         |       +-----------------------+
      +----------+         |       | ptr to typeinfo for D |
      |     d    |         +-----> +-----------------------+
      +----------+                 |         C::x()        |
      |  vtable  |----+            +-----------------------+
      +----------+    |            |    0 (vbase_offset)   |
      |     a    |    |            +-----------------------+
      +----------+    |            |   -20 (top_offset)    |
                      |            +-----------------------+
                      |            | ptr to typeinfo for D |
                      +----------> +-----------------------+
                                   |         A::v()        |
                                   +-----------------------+

Хорошо.Итак, вы видите, что A теперь встроен в D практически так же, как другие базы.Но он встроен в D, а не в начальные классы.

13 голосов
/ 19 апреля 2013
THIS IS THE TEXT by Morgan Deters and NOT CC-licensed. 

Морган Детерс Веб-страницы:

ЧАСТЬ 2:

Строительство / Разрушение в присутствии множественного наследования

КакВышеуказанный объект создается в памяти, когда создается сам объектИ как мы можем гарантировать, что частично построенный объект (и его vtable) безопасны для работы с конструкторами?

К счастью, все это обрабатывается очень тщательно для нас.Скажем, мы создаем новый объект типа D (например, через new D).Во-первых, память для объекта выделяется в куче и возвращается указатель.Вызывается конструктор D, но перед выполнением какой-либо D -специфической конструкции он вызывает конструктор A объекта (после настройки указателя this, конечно!).Конструктор A заполняет часть A объекта D, как если бы он был экземпляром A.

d --> +----------+
      |          |
      +----------+
      |          |
      +----------+
      |          |
      +----------+
      |          |       +-----------------------+
      +----------+       |     0 (top_offset)    |
      |          |       +-----------------------+
      +----------+       | ptr to typeinfo for A |
      |  vtable  |-----> +-----------------------+
      +----------+       |         A::v()        |
      |    a     |       +-----------------------+
      +----------+

Управление возвращается конструктору D,который вызывает конструктор B.(Регулировка указателя здесь не требуется.) Когда конструктор B сделан, объект выглядит следующим образом:

 B-in-D
                          +-----------------------+
                          |   20 (vbase_offset)   |
                          +-----------------------+
                          |     0 (top_offset)    |
                          +-----------------------+
d --> +----------+        | ptr to typeinfo for B |
      |  vtable  |------> +-----------------------+
      +----------+        |         B::w()        |
      |    b     |        +-----------------------+
      +----------+        |    0 (vbase_offset)   |
      |          |        +-----------------------+
      +----------+        |   -20 (top_offset)    |
      |          |        +-----------------------+
      +----------+        | ptr to typeinfo for B |
      |          |   +--> +-----------------------+
      +----------+   |    |         A::v()        |
      |  vtable  |---+    +-----------------------+
      +----------+
      |    a     |
      +----------+

Но подождите ... Конструктор B изменил A часть объекта путем изменения его указателя vtable!Как он узнал, что нужно отличать этот вид B-in-D от B-in-что-то еще (или, в этом отношении, автономного B)?Просто. таблица виртуальных таблиц сказала это сделать.Эта структура, сокращенно VTT , представляет собой таблицу таблиц, используемых в строительстве.В нашем случае VTT для D выглядит так:

B-in-D
                                               +-----------------------+
                                               |   20 (vbase_offset)   |
            VTT for D                          +-----------------------+
+-------------------+                          |     0 (top_offset)    |
|    vtable for D   |-------------+            +-----------------------+
+-------------------+             |            | ptr to typeinfo for B |
| vtable for B-in-D |-------------|----------> +-----------------------+
+-------------------+             |            |         B::w()        |
| vtable for B-in-D |-------------|--------+   +-----------------------+
+-------------------+             |        |   |    0 (vbase_offset)   |
| vtable for C-in-D |-------------|-----+  |   +-----------------------+
+-------------------+             |     |  |   |   -20 (top_offset)    |
| vtable for C-in-D |-------------|--+  |  |   +-----------------------+
+-------------------+             |  |  |  |   | ptr to typeinfo for B |
|    vtable for D   |----------+  |  |  |  +-> +-----------------------+
+-------------------+          |  |  |  |      |         A::v()        |
|    vtable for D   |-------+  |  |  |  |      +-----------------------+
+-------------------+       |  |  |  |  |
                            |  |  |  |  |                         C-in-D
                            |  |  |  |  |      +-----------------------+
                            |  |  |  |  |      |   12 (vbase_offset)   |
                            |  |  |  |  |      +-----------------------+
                            |  |  |  |  |      |     0 (top_offset)    |
                            |  |  |  |  |      +-----------------------+
                            |  |  |  |  |      | ptr to typeinfo for C |
                            |  |  |  |  +----> +-----------------------+
                            |  |  |  |         |         C::x()        |
                            |  |  |  |         +-----------------------+
                            |  |  |  |         |    0 (vbase_offset)   |
                            |  |  |  |         +-----------------------+
                            |  |  |  |         |   -12 (top_offset)    |
                            |  |  |  |         +-----------------------+
                            |  |  |  |         | ptr to typeinfo for C |
                            |  |  |  +-------> +-----------------------+
                            |  |  |            |         A::v()        |
                            |  |  |            +-----------------------+
                            |  |  |
                            |  |  |                                    D
                            |  |  |            +-----------------------+
                            |  |  |            |   20 (vbase_offset)   |
                            |  |  |            +-----------------------+
                            |  |  |            |     0 (top_offset)    |
                            |  |  |            +-----------------------+
                            |  |  |            | ptr to typeinfo for D |
                            |  |  +----------> +-----------------------+
                            |  |               |         B::w()        |
                            |  |               +-----------------------+
                            |  |               |         D::y()        |
                            |  |               +-----------------------+
                            |  |               |   12 (vbase_offset)   |
                            |  |               +-----------------------+
                            |  |               |    -8 (top_offset)    |
                            |  |               +-----------------------+
                            |  |               | ptr to typeinfo for D |
                            +----------------> +-----------------------+
                               |               |         C::x()        |
                               |               +-----------------------+
                               |               |    0 (vbase_offset)   |
                               |               +-----------------------+
                               |               |   -20 (top_offset)    |
                               |               +-----------------------+
                               |               | ptr to typeinfo for D |
                               +-------------> +-----------------------+
                                               |         A::v()        |
                                               +-----------------------+

Конструктор D передает указатель на VTT D на конструктор B (в этом случае он передает адрес первого B-in-D запись).И действительно, vtable, который использовался для макета объекта выше, является специальным vtable, используемым только для построения B-in-D.

Элемент управления возвращается в конструктор D и вызывает конструктор C(с параметром адреса VTT, указывающим на запись "C-in-D + 12").Когда конструктор C завершает работу с объектом, он выглядит следующим образом:

 B-in-D
                                                        +-----------------------+
                                                        |   20 (vbase_offset)   |
                                                        +-----------------------+
                                                        |     0 (top_offset)    |
                                                        +-----------------------+
                                                        | ptr to typeinfo for B |
                    +---------------------------------> +-----------------------+
                    |                                   |         B::w()        |
                    |                                   +-----------------------+
                    |                          C-in-D   |    0 (vbase_offset)   |
                    |       +-----------------------+   +-----------------------+
d --> +----------+  |       |   12 (vbase_offset)   |   |   -20 (top_offset)    |
      |  vtable  |--+       +-----------------------+   +-----------------------+
      +----------+          |     0 (top_offset)    |   | ptr to typeinfo for B |
      |    b     |          +-----------------------+   +-----------------------+
      +----------+          | ptr to typeinfo for C |   |         A::v()        |
      |  vtable  |--------> +-----------------------+   +-----------------------+
      +----------+          |         C::x()        |
      |    c     |          +-----------------------+
      +----------+          |    0 (vbase_offset)   |
      |          |          +-----------------------+
      +----------+          |   -12 (top_offset)    |
      |  vtable  |--+       +-----------------------+
      +----------+  |       | ptr to typeinfo for C |
      |    a     |  +-----> +-----------------------+
      +----------+          |         A::v()        |
                            +-----------------------+

Как видите, конструктор C снова изменил указатель vtable встроенного A-объекта. Встроенные объекты C и A теперь используют специальную конструкцию C-in-D vtable, а внедренный объект B использует специальную конструкцию B-in-D vtable.Наконец, конструктор D завершает работу, и мы получаем ту же диаграмму, что и раньше:

                                   +-----------------------+
                                   |   20 (vbase_offset)   |
                                   +-----------------------+
                                   |     0 (top_offset)    |
                                   +-----------------------+
                                   | ptr to typeinfo for D |
                      +----------> +-----------------------+
d --> +----------+    |            |         B::w()        |
      |  vtable  |----+            +-----------------------+
      +----------+                 |         D::y()        |
      |     b    |                 +-----------------------+
      +----------+                 |   12 (vbase_offset)   |
      |  vtable  |---------+       +-----------------------+
      +----------+         |       |    -8 (top_offset)    |
      |     c    |         |       +-----------------------+
      +----------+         |       | ptr to typeinfo for D |
      |     d    |         +-----> +-----------------------+
      +----------+                 |         C::x()        |
      |  vtable  |----+            +-----------------------+
      +----------+    |            |    0 (vbase_offset)   |
      |     a    |    |            +-----------------------+
      +----------+    |            |   -20 (top_offset)    |
                      |            +-----------------------+
                      |            | ptr to typeinfo for D |
                      +----------> +-----------------------+
                                   |         A::v()        |
                                   +-----------------------+

Разрушение происходит таким же образом, но в обратном порядке.Деструктор D вызывается.После запуска кода уничтожения пользователя деструктор вызывает деструктор C и предписывает ему использовать соответствующую часть VTT D.Деструктор Си манипулирует указателями vtable так же, как и во время конструирования;то есть соответствующие указатели vtable теперь указывают на v-конструкцию C-in-D.Затем он запускает код уничтожения пользователя для C и возвращает управление деструктору D, который затем вызывает деструктор B со ссылкой на VTT D.Деструктор B устанавливает соответствующие части объекта для ссылки на виртуальную таблицу конструкции B-in-D.Он запускает код уничтожения пользователя для B и возвращает управление деструктору D, который, наконец, вызывает деструктор A.Деструктор A изменяет виртуальную таблицу для части A объекта, чтобы она ссылалась на виртуальную таблицу для A. Наконец, управление возвращается к деструктору D, и уничтожение объекта завершено.Память, однажды использованная объектом, возвращается в систему.

Теперь, на самом деле, история несколько сложнее. Вы когда-нибудь видели эти «ответственные» и «не отвечающие» спецификации конструкторов и деструкторов в сообщениях о предупреждениях и ошибках, создаваемых GCC, или в двоичных файлах, создаваемых GCC? Дело в том, что может быть две реализации конструктора и до трех реализаций деструктора.

Конструктор "ответственный" (или полный объект) - это конструктор виртуальных баз, а конструктор "не отвечающий" (или базовый объект) - это конструктор, который этого не делает. Рассмотрим наш пример выше. Если B создается, его конструктор должен вызвать конструктор A, чтобы построить его. Точно так же конструктор C должен построить A. Однако, если B и C построены как часть конструкции D, их конструкторы не должны конструировать A, потому что A является виртуальной базой, а конструктор D позаботится о ее построении ровно один раз. для случая D. Рассмотрим случаи:

Если вы делаете новый A, вызывается «ответственный» конструктор A для создания A. Когда вы делаете новый B, вызывается «ответственный» конструктор B. Он вызовет конструктор "не отвечает" для A.

новый C похож на новый B.

Новый D вызывает конструктор D "ответственный". Прошелся по этому примеру. «Ответственный» конструктор D вызывает «незавершенные» версии конструкторов A, B и C (в том порядке).

Деструктор "в зарядке" является аналогом конструктора "в плате" - он отвечает за разрушение виртуальных баз. Точно так же деструктор "не отвечает" генерируется. Но есть и третий. Деструктор «ответственного за удаление» - это тот, который освобождает хранилище, а также разрушает объект. Так когда же один из них вызывается по отношению к другому?

Ну, есть два типа объектов, которые могут быть разрушены - те, которые расположены в стеке, и те, которые размещены в куче. Рассмотрим этот код (учитывая нашу алмазную иерархию с виртуальным наследованием ранее):

D d;            // allocates a D on the stack and constructs it
D *pd = new D;  // allocates a D in the heap and constructs it
/* ... */
delete pd;      // calls "in-charge deleting" destructor for D
return;         // calls "in-charge" destructor for stack-allocated D

Мы видим, что фактический оператор удаления вызывается не кодом, выполняющим удаление, а скорее деструктором удаления для удаляемого объекта. Почему это так? Почему бы не вызвать вызывающего деструктора, а затем удалить объект? Тогда у вас будет только две копии реализации деструктора вместо трех ...

Хорошо, компилятор мог бы сделать такую ​​вещь, но это было бы более сложным по другим причинам. Рассмотрим этот код (в предположении виртуального деструктора, который вы всегда используете, верно? ... правильно?!?):

D *pd = new D;  // allocates a D in the heap and constructs it
    C *pc = d;      // we have a pointer-to-C that points to our heap-allocated D
    /* ... */
    delete pc;      // call destructor thunk through vtable, but what about delete?

Если у вас не было варианта «ответственного удаления» деструктора D, то операция удаления должна была бы настроить указатель так же, как это делает деструктор thunk. Помните, объект C встроен в D, и поэтому наш указатель на C выше настроен так, чтобы указывать на середину объекта D. Мы не можем просто удалить этот указатель, так как это не указатель, который был возвращено malloc() когда мы его построили.

Итак, если бы у нас не было ответственного за удаление деструктора, нам потребовалось бы большое спасибо оператору удаления (и представить их в наших таблицах) или что-то подобное.

Thunks, виртуальные и не виртуальные

Этот раздел еще не написан.

Множественное наследование с виртуальными методами на одной стороне

Хорошо. Последнее упражнение Что если у нас есть иерархия наследования алмазов с виртуальным наследованием, как и раньше, но с виртуальными методами только по одной ее стороне? Итак:

class A {
public:
  int a;
};

class B : public virtual A {
public:
  int b;
  virtual void w();
};

class C : public virtual A {
public:
  int c;
};

class D : public B, public C {
public:
  int d;
  virtual void y();
};

В этом случае макет объекта выглядит следующим образом:

                                   +-----------------------+
                                   |   20 (vbase_offset)   |
                                   +-----------------------+
                                   |     0 (top_offset)    |
                                   +-----------------------+
                                   | ptr to typeinfo for D |
                      +----------> +-----------------------+
d --> +----------+    |            |         B::w()        |
      |  vtable  |----+            +-----------------------+
      +----------+                 |         D::y()        |
      |     b    |                 +-----------------------+
      +----------+                 |   12 (vbase_offset)   |
      |  vtable  |---------+       +-----------------------+
      +----------+         |       |    -8 (top_offset)    |
      |     c    |         |       +-----------------------+
      +----------+         |       | ptr to typeinfo for D |
      |     d    |         +-----> +-----------------------+
      +----------+
      |     a    |
      +----------+

Таким образом, вы можете видеть подобъект C, который не имеет виртуальных методов, все еще имеет vtable (хотя и пустой). Действительно, все экземпляры C имеют пустой vtable.

Спасибо, Морган Детерс !!

5 голосов
/ 07 июня 2011

Таблица виртуальных таблиц (VTT).Это позволяет безопасно создавать / деконструировать объекты при множественном наследовании.

для объяснения см .: http://www.cse.wustl.edu/~mdeters/seminar/fall2005/mi.html

2 голосов
/ 07 июня 2011

VTT = таблица виртуальных таблиц.

См. Представления классов C ++ .

2 голосов
/ 07 июня 2011

Это виртуальная (функциональная) таблица и таблица виртуальных типов, которая обрабатывает множественное наследование.


cf:
http://www.codesourcery.com/archives/cxx-abi-dev/msg00077.html http://www.cse.wustl.edu/~mdeters/seminar/fall2005/mi.html

1 голос
/ 07 июня 2011

Таблица виртуальных таблиц, сокращенно VTT, представляет собой таблицу виртуальных таблиц, используемых в конструкции, где используется множественное наследование.

Дополнительная информация в этой интересной статье: Замечания о множественном наследовании в компиляторе GCC C ++ v4.0.1

...