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