Сложное динамическое приведение в 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
базового класса подобъекта из