Сложное динамическое приведение в c++
у меня есть следующий случай в C++:
- абстрактные базовые классы
Abstract1иAbstract2. Они не связаны. - класс
Fooвывод из обоихAbstract1иAbstract2
я в единице компиляции, где у меня нет информации о классе Foo (нет объявления, нет определения). Только Abstract1 и Abstract2 известны.
(На самом деле, Foo даже определяется в DLL)
позволит ли dynamic_cast кастинг из Abstract1* к Abstract2*? Это стандарт?
4 ответов
то, что вы описываете, является так называемым кросс-литой. Для dynamic_cast<T>(v), стандарт указывает в [expr.активный.cast] / 8
если
C- Это тип класса, к которомуTуказывает или ссылается, во время выполнения проверка логически выполняется следующим образом:
если в самом производном объекте указано (указано)
v,vуказывает (ссылается) на открытый субобъект базового класса
Да, это будет работать.
dynamic_cast на основе RTTI. Информации, предоставленной RTTI здесь достаточно для определения фактического динамического типа указанного объекта. По определению RTTI является понятием времени выполнения, как и динамический тип указанного объекта (тот факт, что определение Foo недоступно в единице компиляции, где написано указанное приведение, является понятием времени компиляции, не имеющим здесь никакого отношения).
- если указано на объект на самом деле Foo, dynamic_cast преуспеет в времени.
- если это не указатель на объект, производный от Abstract2, он потерпит неудачу (возвращая нулевой указатель).
подробности
возможная реализация dynamic_cast - это поиск специального элемента в начале макета памяти объекта (или он может быть сохранен вдоль v-таблицы). Эта структура может содержать значение, которое идентифицирует динамический тип объекта.
Где-то компилятор создал бы static таблица, реплицирующая всю информацию о диаграмме наследования вашей программы.
Во время выполнения приведение извлекает идентификатор типа вашего экземпляра и проверяет его на статическую таблицу. Если этот идентификатор ссылается на тип, производный от Abstract2, приведение имеет смысл (и код может возвращать указатель, правильно смещенный к Abstract2 интерфейс вашего объекта).
даже этот наивная реализация никогда не требует знания Foo в единице компиляции, где
литые написано.
этот код:
void func(Abstract1* a1)
{
Abstract2* a2 = dynamic_cast<Abstract2*>(a1);
...
}
ты спрашиваешь:
если a1 указывает Foo object, будет ли динамическое приведение возвращать допустимый указатель объекта?
да:
- во время выполнения dynamic-cast определит V-таблицу
a1как V-таблицаclass Foo. - С
class Fooнаследует отclass Abstract2, dynamic-cast вернет допустимый указатель.
Ну, вы могли бы просто пробовал!
#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базового класса подобъекта из