У меня есть следующий случай в C ++:
Abstract1
а также Abstract2
, Они не связаны.Foo
вытекающий из обоих Abstract1
а также Abstract2
Я нахожусь в модуле компиляции, где у меня нет информации о классе Foo
(без декларации, без определения). Только Abstract1
а также Abstract2
известны.
(На самом деле, Foo даже определен в DLL)
Будет ли dynamic_cast разрешать трансляцию из Abstract1*
в Abstract2*
? Это стандарт?
То, что вы описываете, является так называемым кросс-бросок. За dynamic_cast<T>(v)
стандарт указывает в [expr.dynamic.cast] / 8
Если
C
это тип класса, к которомуT
указывает или ссылается, время выполнения
Проверка логически выполняется следующим образом:
Если в самом производном объекте указано (указано) на
v
,v
указывает (относится) к подобъекту публичного базового классаC
объект [..]В противном случае, если
v
указывает (относится) кpublic
подобъект базового класса самого производного объекта и тип самого производного объекта
имеет базовый класс, типаC
однозначно иpublic
,
результат указывает (относится) кC
подобъект самого производного
объект.
Это будет работать даже без информации о Foo
существование в переводческой единице, которая содержит актерский состав.
Вы должны проверить этот вопрос тоже.
Да, это будет работать.
dynamic_cast
основывается на RTTI. Информации, предоставленной RTTI, здесь достаточно для определения фактического динамического типа указанного объекта. По определению RTTI является понятием времени выполнения, так же как и динамический тип указанного объекта (тот факт, что определение Foo недоступно в модуле компиляции, в котором написано приведение, является понятием времени компиляции, здесь не имеющим отношения к делу).
Возможная реализация dynamic_cast
это поиск специального члена в начале макета памяти объекта (или он может быть сохранен вдоль v-таблицы). Эта структура может содержать значение, которое идентифицирует динамический тип объекта.
Где-то компилятор сгенерировал бы статический Таблица, тиражирующая всю информацию о вашей программе наследственной диаграммы.
Во время выполнения приведение извлечет идентификатор типа вашего экземпляра и сверит его со статической таблицей. Если этот идентификатор ссылается на тип, производный от Abstract2, приведение имеет смысл (и код может вернуть указатель, правильно смещенный к Abstract2
интерфейс вашего объекта).
Даже эта наивная реализация никогда не требует знания Foo
в блоке компиляции, где
актерский состав написан.
Для этого кода:
void func(Abstract1* a1)
{
Abstract2* a2 = dynamic_cast<Abstract2*>(a1);
...
}
Вы спрашиваете:
Если a1
указывает на Foo
объект, будет ли динамическое приведение возвращать действительный указатель объекта?
Ответ да:
a1
как V-таблица class Foo
,class Foo
наследуется от class Abstract2
динамическое приведение вернет действительный указатель.Ну, вы могли бы просто иметь попробовал это!
#include <cassert>
struct IBase1
{
virtual void foo() = 0;
virtual ~IBase1() {}
};
struct IBase2
{
virtual void bar() = 0;
virtual ~IBase2() {}
};
struct Derived : IBase1, IBase2
{
void foo() {}
void bar() {}
};
int main()
{
Derived d;
IBase1* ptr = &d;
assert(dynamic_cast<IBase2*>(ptr));
assert(dynamic_cast<Derived*>(ptr));
}
// Compiles successfully
И вот доказательство:
[C++11: 5.2.7/8]:
ЕслиC
это тип класса, к которомуT
указывает или ссылается, проверка во время выполнения логически выполняется следующим образом:
- Если в самом производном объекте указано (указано) на
v
,v
указывает (относится) кpublic
подобъект базового классаC
объект, и если только один объект типаC
происходит от подобъекта, на который указывает (ссылается)v
результат указывает (относится) к этомуC
объект.Иначе, если
v
указывает (ссылается) на публичный подобъект базового класса самого производного объекта, а тип самого производного объекта имеет базовый класс типаC
однозначно иpublic
, результат указывает (относится) кC
подобъект самого производного объекта.В противном случае проверка во время выполнения терпит неудачу.
В разговорной речи мы называем это кросс-литье.
Язык не требует, чтобы тип самого производного объекта был известен в «текущей» единице перевода; это зависит от реализации, чтобы это работало, и в общей модели «виртуальных таблиц» это действительно так.