Детали реализации виртуальной отправки
Вопрос
Прежде всего, я хочу прояснить, что Я понимаю, что нет понятия vtables и vptrs в стандарте C ++. Отказ Однако я думаю, что практически все реализации реализуют виртуальный механизм отправки в значительной степени так же (поправьте меня, если я ошибаюсь, но это не главный вопрос). Кроме того, я верю Я знаю, как работают виртуальные функции, То есть я всегда могу сказать, какая функция будет называться, мне просто нужны детали реализации.
Предположим, кто-то спросил меня следующее:
«У вас есть базовый класс B с виртуальными функциями v1, v2, v3 и полученный класс d: b, который переопределяет функции v1 и v3 и добавляет виртуальную функцию v4. Объясните, как работает виртуальные рассылки».
Я бы ответил так:
Для каждого класса с виртуальными функциями (в данном случае B и d) у нас есть отдельный массив указателей к функциям, называемым VTable.
VTable для B будет содержать
&B::v1
&B::v2
&B::v3
VTable для D будет содержать
&D::v1
&B::v2
&D::v3
&D::v4
Теперь класс B содержит указатель участника VPTR. D Естественно наследует его и поэтому содержит его тоже. В конструкторе и деструктора BB устанавливает VPTR, чтобы указать на VTable B. В конструкторе и деструктор DD устанавливает его, чтобы указать на VTable.
Любой призыв к виртуальной функции F на объекте X полиморфного класса X интерпретируется как вызов X.VPTR [положение F в VTTable
Вопросы:
1. Есть ли у меня какие-либо ошибки в приведенном выше описании?
2. Как компилятор знает позицию F в VTable (подробно, пожалуйста)
3. Значит ли это, что если у класса есть два база, то у него есть два VPTR? Что происходит в этом случае? (Попробуйте описать аналогичным образом, так как я сделал как можно больше деталей)
4. Что происходит в алмазной иерархии с на вершине B, C в середине и D на дне? (A - это виртуальный базовый класс B и C)
Заранее спасибо.
Решение
1. Есть ли у меня какие-либо ошибки в приведенном выше описании?
Все хорошо. :-)
2. Как компилятор знает позицию F в VTable
Каждый поставщик будет иметь свой собственный способ сделать это, но я всегда думаю о VTable в качестве карты подписи функции участника к смещению памяти. Таким образом, компилятор просто поддерживает этот список.
3. Значит ли это, что если у класса есть два база, то у него есть два VPTR? Что происходит в этом случае?
Как правило, компиляторы составляют новый VTable, который состоит из всех VTBable из виртуальных баз, добавленных вместе в том порядке, наряду с VTable указателем виртуальной базы. Они следуют за этим с помощью VTable функций полученного класса. Это очень сильно специфический поставщик, но для class D : B1, B2
, вы обычно видите D._vptr[0] == B1._vptr
.
Это изображение фактически для составления полей элементов объекта, но VTables может быть составлен компилятором точно так же (насколько я понимаю).
4. Что происходит в алмазной иерархии с на вершине B, C в середине и D на дне? (A - это виртуальный базовый класс B и C)
Короткий ответ? Абсолютный ад Вы практически наследули как базы? Только один из них? Никто из них? В конечном итоге используются те же методы составления VTable для класса, но как это делается варьируется в зависимости от способа дико, поскольку как Это должно быть сделано совсем не набор в камне. Существует достойное объяснение решения проблемы алмазной иерархии здесь, но, как и большинство из этого, это довольно поставщик.
Другие советы
- Выглядит хорошо для меня
Конкретная реализация, но большинство из них только в порядке исходного кода - то есть порядок, который они появляются в классе - начиная с базового класса, затем добавляя новые виртуальные функции из полученного. Пока компилятор имеет детерминированный способ сделать это, то все, что он хочет сделать, это в порядке. Однако в Windows, для создания COM совместимых V-таблиц, он должен быть в порядке источника
(Точно сказать не могу)
- (Угадайте) Алмаз просто означает, что у вас могут быть два копии базового класса B. Виртуальное наследование будет объединять их в один экземпляр. Поэтому, если вы установите члена через D1, вы можете прочитать его через D2. (С C, полученным из D1, D2, каждый из них получен из б). Я считаю, что в обоих случаях VTables будет идентична, поскольку указатели функции одинаковы - память для членов данных - это то, что объединено.
Комментарии:
Я не думаю, что разрушители приходят в это!
Звонок, такой как например,
D d; d.v1();
Вероятно, не будет реализован через vTable, так как компилятор может разрешить адрес функции при компиляции / канала-времени.Компилятор знает
f
Положение, потому что он там положил!Да, класс с несколькими базовыми классами обычно будет иметь несколько VPTR (при условии виртуальных функций в каждом базовом классе).
Скотт Мейерс «Эффективные C ++» книги «C ++» объясняют многократное наследование и бриллианты лучше, чем я могу; Я бы порекомендовал читать их для этого (и многих других) причин. Рассмотрим их важное чтение!