Виртуальное множественное наследование и приведение

Я попытался создать класс, который наследует от нескольких классов, как показано ниже, получив «алмаз»
(D наследует от B и C. B и C оба наследуют от A фактически):

 
  / \
ДО НАШЕЙ ЭРЫ
  \ /
  D

Теперь у меня есть контейнер со связанным списком, который содержит указатели на базовый класс ().
Когда я попытался сделать явное приведение к указателю (после проверки typeid), я получил следующую ошибку:
«невозможно преобразовать указатель на базовый класс« A »в указатель на производный класс« D »- базовый класс является виртуальным»

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

0

Решение

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

Следовательно, невозможно определить наиболее производный объект, для которого вам предоставляется виртуальная база во время компиляции, поскольку связь между базой и наиболее производным объектом не является фиксированной. Вы должны ждать, пока вы не знаете, какие фактический объект находится прежде, чем вы сможете решить, где он находится по отношению к основанию. Это то, что делает динамический актерский состав.

3

Другие решения

Когда я попытался сделать явное приведение к указателю (после проверки typeid)

После успешного typeid(x) == typeid(T) Вы знаете динамический тип xи вы можете теоретически избежать любой другой проверки времени выполнения, связанной с dynamic_cast в таком случае. OTOH, компилятор не обязан делать этот вид статического анализа.

static_cast<T&>(x) не передает компилятору знания о том, что динамический тип x на самом деле T: предварительное условие слабее (что T объект имеет x как подобъект базового класса).

C ++ может обеспечить static_exact_cast<T&>(x) который действителен только если x обозначает объект динамического типа T (а не какой-то тип, полученный из T, В отличие от static_cast<T&>). Это гипотетически static_exact_cast<T&>(x), предполагая динамический тип x является T, пропустит любую проверку во время выполнения и вычислит правильный адрес из знания T макет объекта: потому что в

D d;
B &br = d;

вычисление смещения во время выполнения не требуется, в static_exact_cast<D&>(br) обратная настройка не будет включать вычисления смещения во время выполнения.

Дано

B &D_to_B (D &dr) {
return dr;
}

вычисление смещения во время выполнения необходимо в D_to_B (кроме случаев, когда весь анализ программы показывает, что ни один класс не получен из D имеет различное смещение базового класса A); дано

struct E1 : virtual A
struct E2 : virtual A
struct F : E1, D, E2

макет D подобъект F будет отличаться от макета D завершенный объект: A подобъект будет с другим смещением. Смещение, необходимое для D_to_B будет дано vtable из D (или хранится непосредственно в объекте); это означает, что D_to_B не будет просто включать постоянное смещение в качестве простого «статического» преобразования (перед входом в конструктор объекта vptr не настроен, поэтому такое приведение не может работать; будьте осторожны с приведением вверх в списке инициаторов конструктора).

И кстати, D_to_B (d) не отличается от static_cast<B&> (d), так что вы видите, что вычисление смещения во время выполнения может быть сделано внутри static_cast,

Рассмотрим следующий код, который компилируют наивно ar имеет динамический тип F):

F f;
D &dr = f; // static offset
A &ar = dr; // runtime offset
D &dr2 = dynamic_cast<D&>(ar);

Нахождение A базовый класс предмет из ссылки на D (значение неизвестного динамического типа) требует проверки во время выполнения виртуальной таблицы (или ее эквивалента). Возвращаясь к D подобъект требует нетривиального вычисления:

  • выяснение адреса полного объекта (который бывает типа F), используя vtable of A
  • выяснение адреса однозначного и публично полученного D подобъект базового класса fснова используя vtable

Это не тривиально, как dynamic_cast<D&>(ar) статически не знает ничего конкретного F (макет FМакет виртуальной таблицы F); все взято из vtable. Все dynamic_cast знает, что есть производный класс A и у vtable есть вся информация.

Конечно, нет static_exact_cast<> в C ++, поэтому вы должны использовать dynamic_castс соответствующими проверками во время выполнения; dynamic_cast является сложной функцией, но сложность охватывает случаи базового класса; когда динамический тип задан dynamic_castобход дерева базовых классов исключается, и тест довольно прост.

Заключение:

Либо вы называете динамический тип в dynamic_cast<T> а также dynamic_cast это быстро и просто в любом случае, или вы не делаете, и сложность dynamic_cast действительно нужно.

0

По вопросам рекламы [email protected]