полиморфизм — сложный dynamic_cast в переполнении стека

У меня есть следующий случай в C ++:

  • Абстрактные базовые классы Abstract1 а также Abstract2, Они не связаны.
  • Класс Foo вытекающий из обоих Abstract1 а также Abstract2

Я нахожусь в модуле компиляции, где у меня нет информации о классе Foo (без декларации, без определения). Только Abstract1 а также Abstract2 известны.
(На самом деле, Foo даже определен в DLL)

Будет ли dynamic_cast разрешать трансляцию из Abstract1* в Abstract2*? Это стандарт?

10

Решение

То, что вы описываете, является так называемым кросс-бросок. За dynamic_cast<T>(v)стандарт указывает в [expr.dynamic.cast] / 8

Если C это тип класса, к которому T указывает или ссылается, время выполнения
Проверка логически выполняется следующим образом:

  • Если в самом производном объекте указано (указано) на v, v указывает (относится) к подобъекту публичного базового класса C объект [..]

  • В противном случае, если v указывает (относится) к public подобъект базового класса самого производного объекта и тип самого производного объекта
    имеет базовый класс, типа Cоднозначно и public,
    результат указывает (относится) к C подобъект самого производного
    объект.

Это будет работать даже без информации о Fooсуществование в переводческой единице, которая содержит актерский состав.

Вы должны проверить этот вопрос тоже.

6

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

Да, это будет работать.

dynamic_cast основывается на RTTI. Информации, предоставленной RTTI, здесь достаточно для определения фактического динамического типа указанного объекта. По определению RTTI является понятием времени выполнения, так же как и динамический тип указанного объекта (тот факт, что определение Foo недоступно в модуле компиляции, в котором написано приведение, является понятием времени компиляции, здесь не имеющим отношения к делу).

  • Если указанный объект на самом деле является Foo, dynamic_cast будет успешным в время выполнения.
  • Если это не указатель на объект, производный от Abstract2, он потерпит неудачу (возвращая нулевой указатель).

Возможная реализация dynamic_cast это поиск специального члена в начале макета памяти объекта (или он может быть сохранен вдоль v-таблицы). Эта структура может содержать значение, которое идентифицирует динамический тип объекта.
Где-то компилятор сгенерировал бы статический Таблица, тиражирующая всю информацию о вашей программе наследственной диаграммы.
Во время выполнения приведение извлечет идентификатор типа вашего экземпляра и сверит его со статической таблицей. Если этот идентификатор ссылается на тип, производный от Abstract2, приведение имеет смысл (и код может вернуть указатель, правильно смещенный к Abstract2 интерфейс вашего объекта).

Даже эта наивная реализация никогда не требует знания Foo в блоке компиляции, где
актерский состав написан.

4

Для этого кода:

void func(Abstract1* a1)
{
Abstract2* a2 = dynamic_cast<Abstract2*>(a1);
...
}

Вы спрашиваете:

Если a1 указывает на Foo объект, будет ли динамическое приведение возвращать действительный указатель объекта?

Ответ да:

  • Во время выполнения динамическое приведение идентифицирует V-таблицу a1 как V-таблица class Foo,
  • поскольку class Foo наследуется от class Abstract2динамическое приведение вернет действительный указатель.
2

Ну, вы могли бы просто иметь попробовал это!

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

  • В противном случае проверка во время выполнения терпит неудачу.

В разговорной речи мы называем это кросс-литье.

Язык не требует, чтобы тип самого производного объекта был известен в «текущей» единице перевода; это зависит от реализации, чтобы это работало, и в общей модели «виртуальных таблиц» это действительно так.

1