Рассмотрение конструкции C++ dynamic_cast также проверки указателя или ссылочного типа

Когда мы делаем динамическое приведение:

A* x = ...;
B* b = dynamic_cast<B*>(x);

dynamic_cast вернет действительный указатель только в том случае, если:

  1. A является полиморфным, иначе компиляция завершится ошибкой.
  2. B эквивалентен A или является производным от A B должен иметь связь с A в иерархии наследования (хотя я не уверен в стратегии), иначе возвращает nullptr. Это обновляется в соответствии с комментариями.
  3. RTTI *x показывает, что это объект класса B или производный класс B, в противном случае возвращает nullptr.

Я рассматриваю случай, когда соблюдаются и условие 1, и условие 3, кроме условия 2. Это может быть возможно из-за копирования памяти или переинтерпретации_приведения и т. д. Все последующие обсуждения основаны на этом сценарии.

Вот небольшой пример: http://ideone.com/tBctgT.

Могу ли я сказать, что: если бы в такой ситуации C++ разрешил успешное выполнение dynamic_cast, было бы безопасно использовать указатель, возвращаемый dynamic_cast? Если да, то почему стандарт С++ определяет, что проверка условия 2 является обязательной?

Кроме того, условие 2 можно проверить во время компиляции. Почему стандарт C++ определяет возврат nullptr во время выполнения вместо выдачи ошибки компиляции, если это не считается правильной операцией?


person deltamaster    schedule 25.05.2014    source источник
comment
(2) неверно. А и Б вполне могут быть не связаны. x может относиться к классу C, производному как от A, так и от B.   -  person n. 1.8e9-where's-my-share m.    schedule 25.05.2014
comment
@н.м. Извините, я не совсем уловил вашу мысль. Я пробовал в случае, когда A не связан с B, а * x имеет тип RTTI B, dynamic_cast не удалось во время выполнения.   -  person deltamaster    schedule 26.05.2014
comment
В моем комментарии было что-то про класс C, у вас в примере есть класс C? Демо,   -  person n. 1.8e9-where's-my-share m.    schedule 26.05.2014
comment
Прохладно! Это что-то новое для меня. Дай мне подумать об этом. Вот код, который я пытаюсь запустить: ideone.com/tBctgT. Похоже, тип указателя времени компиляции проверяется, поэтому второе преобразование не удалось, но я не уверен в стратегии.   -  person deltamaster    schedule 26.05.2014
comment
Ваш код имеет неопределенное поведение. Reinterpret-cast определяется только в очень узком наборе обстоятельств, которые не применимы к вашему коду. Вы никогда не должны использовать reinterpret_cast в любой цепочке reinterpret_cast, которая не начинается и не заканчивается одним и тем же типом.   -  person n. 1.8e9-where's-my-share m.    schedule 26.05.2014
comment
Я знаю, что использовать reinterpret_cast здесь не очень хорошая идея, но я просто хочу узнать больше теоретических вещей в дизайне языка. В этом случае содержимое *s и *a побитово идентично, и, конечно же, type_info тоже. Насколько я понимаю, проверка условия 2 не обеспечивает дополнительной безопасности. Если *s уже поврежден, но имеет тот же type_info, что и *a, указатель все еще можно переинтерпретировать_преобразовать обратно к типу A*, и динамическое_приведение будет успешным.   -  person deltamaster    schedule 26.05.2014
comment
Система выполнения должна фактически проверить, что исходный класс (известный статически) на самом деле является общедоступным базовым классом фактического класса RTTI указателя, преобразованного из. В корректной программе это должен быть базовый класс, но не обязательно общедоступный; это дополнительное условие проверяется во время выполнения. Это предписано стандартом. Проверьте реализацию gcc здесь . В стандарте нет условия 2.   -  person n. 1.8e9-where's-my-share m.    schedule 26.05.2014


Ответы (1)


Я думаю, что пример здесь самый простой:

class A { ... };
class B : public A { ... };
class C : public A { ... };

A *x = new C;
B *y = dynamic_cast<B *>(x);

Пункт 1.

А — это класс, он полиморфен.

Пункт 2.

dynamic_cast<>() допускается во время компиляции, поскольку B является производным от A, а x имеет тип A *.

Пункт 3.

dynamic_cast<>() возвращает значение null, поскольку x не представляет объект типа B (или класс, производный от B). Таким образом, во время выполнения код завершается ошибкой, и вы получаете y == nullptr.

person Alexis Wilke    schedule 25.05.2014
comment
Ну, вы говорите о случае, когда выполняются условия 1 и 2, а меня интересует случай, когда выполняются условия 1 и 3. У меня нет вопросов о поведении в вашем случае. - person deltamaster; 26.05.2014
comment
У вас есть пример, который компилируется, а условие 2 неверно? Если это так, вы можете обновить свой вопрос с помощью указанного примера... - person Alexis Wilke; 26.05.2014
comment
Хорошо, я видел вашу ссылку. После reinterpret_cast<>() новый объект имеет тип S, и, таким образом, RTTI соответствует определениям S и ничему другому. Когда вы говорите, что это тип S, вы теряете RTTI других (исходных) типов. C++ основан на C, который тоже очень похож на него. Типы после компиляции становятся довольно субъективными. (т.е. что касается ассемблерного кода, это просто блок данных.) - person Alexis Wilke; 27.05.2014