Dynamic cast c что это

от admin

Динамическая идентификация типов времени выполнения

Динамическая идентификация типов времени выполнения позволяет программе узнать реальный производный тип объекта, адресуемого по ссылке или по указателю на базовый класс. Реализована 2 операциями:

  • dynamic_cast – поддерживает преобразование типов времени выполнения
  • операция typeid идентифицирует реальный тип выражения

Операция dynamic_cast

Встроенная унарная операция dynamic_cast языка C++ позволяет безопасно трансформировать указатель на базовый класс в указатель на производный класс:

При невозможности трансформации возвращается нулевой указатель

Операция dynamic_cast (ссылки)

То же самое можно проделать и с ссылками на объект.

При невозможности ссылочного преобразования генерируется bad_cast исключение. В общем случае все операции трансформации нужно производить ожидая, что может сгенерироваться ошибка. Работу с исключительными операциями рассмотрим позднее.

Операция typeid

Встроенная унарная операция typeid позволяет установить фактический тип любых объектов

Зачем нужен dynamic_cast?

Для чего нужен dynamic_cast ? Какие преимущества имеет запись 1 по сравнению с записью 2 и какие недостатки? В каких случаях какой вариант следует использовать?

Вообще-то, в том варианте, как вы записали, не нужно приводить ничего, потому что объект производного класса уже ЯВЛЯЕТСЯ объектом базового. Так что можно просто писать

А вот если наоборот —

начинаются проблемы. Потому что обычно это говорит о плохом проектировании. Вы хотите использовать именно потомка там, где используется только предок. Т.е. по сути дописать какое-то хитрое поведение там, где о нем ничего не известно и не должно быть известно.

dynamic_cast позволяет не обрушить всю программу потому только, что вы сделали что-то неверно. Ну, например,

Здесь вы вызываете функцию-член, которая есть у Derived , но которой нет в Base . Но что будет, если вы действительно передадите в функцию указтель на Base ? Указатель на другого потомка Base , который ничего об этой функции не знает?

А dynamic_cast позволяет по крайней мере убедиться, что происходит именно то, что нужно, т.е. что там действительно указатель на Derived .

Что до приведения в стиле C — (A*)b — то тут, пожалуй, ближе всего reinterpret_cast — просто рассматривать биты как имеющие иной тип. Без каких-либо проверок. Что уже опасно, но, по крайней мере, в тексте программы в глаза будет бросаться это длинное слово — reinterpret_cast — как указатель на опасность. И это не шутка, это я повторяю Страуструпа — насчет длинных неуклюжих названий . _cast .

18.10 – Динамическое приведение типов

Еще в уроке «8.5 – Явное преобразование (приведение) типов данных и static_cast » мы изучили концепцию преобразования типов и использование static_cast для преобразования переменных из одного типа в другой.

В этом уроке мы продолжим рассмотрение другого типа приведения: dynamic_cast .

Необходимость в dynamic_cast

Имея дело с полиморфизмом, вы часто сталкиваетесь со случаями, когда у вас есть указатель на базовый класс, но вы хотите получить доступ к какой-либо информации, которая существует только в производном классе.

Рассмотрим следующую (слегка надуманную) программу:

В этой программе функция getObject() всегда возвращает указатель Base , но этот указатель может указывать либо на объект Base , либо на объект Derived . В случае, когда указатель указывает на объект Derived , как нам вызвать Derived::getName() ?

Один из способов – добавить в Base виртуальную функцию с именем getName() (чтобы мы могли вызывать ее с помощью указателя/ссылки Base и динамически разрешать ее вызов в Derived::getName() ). Но что бы эта функция вернула, если бы вы вызвали ее с помощью указателя/ссылки Base , которая на самом деле указывала на объект Base ? У него нет никакого осмысленного значения для возврата. Более того, мы загрязнили бы наш класс Base вещами, которые на самом деле должны быть заботой только класса Derived .

Мы знаем, что C++ позволяет вам неявно преобразовать указатель Derived в указатель Base (фактически, getObject() делает именно это). Этот процесс иногда называют повышающим преобразование (upcasting). Однако что, если бы существовал способ преобразовать указатель Base обратно в указатель Derived ? Тогда мы могли бы вызвать Derived::getName() напрямую, используя этот указатель, и вообще не беспокоиться о добавлении виртуальной функции.

dynamic_cast

C++ предоставляет оператор приведения с именем dynamic_cast , который можно использовать только для этой цели. Хотя динамическое приведение имеет несколько различных возможностей, наиболее распространенное использование динамического приведения – преобразование указателей базового класса в указатели производного класса. Этот процесс называется понижающим преобразованием (downcasting).

Использование dynamic_cast работает так же, как static_cast . Вот наш пример main() выше, с использованием dynamic_cast для преобразования нашего указателя Base обратно в указатель Derived :

Этот код печатает:

Сбой dynamic_cast

Приведенный выше пример работает, потому что b фактически указывает на объект Derived , поэтому преобразование b в указатель Derived проходит успешно.

Однако мы сделали довольно опасное предположение, что b указывает на объект Derived . Что, если бы b не указывал на объект Derived ? Это легко проверить, изменив аргумент getObject() с true на false . В этом случае getObject() вернет указатель Base на объект Base . Когда мы попытаемся выполнить динамическое преобразование в объект Derived , это не удастся, потому что это преобразование не может быть выполнено.

Если dynamic_cast завершается неудачей, результатом преобразования будет нулевой указатель.

Читать:
Какие операции или операция относятся к бинарным

Поскольку мы не проверили результат на нулевой указатель, мы обращаемся к d->getName() , что пытается разыменовать нулевой указатель, что приводит к неопределенному поведению (возможно, к сбою).

Чтобы сделать эту программу безопасной, нам нужно убедиться, что результат выполнения dynamic_cast действительно успешен:

Правило

Всегда проверяйте результат выполнения динамического приведения, проверяя результат на нулевой указатель.

Обратите внимание: поскольку dynamic_cast во время выполнения выполняет какую-то проверку согласованности (чтобы гарантировать, что преобразование может быть выполнено), использование dynamic_cast приводит к снижению производительности.

Также обратите внимание, что есть несколько случаев, когда понижающее преобразование с использованием dynamic_cast не работает:

  1. с защищенным или закрытым наследованием;
  2. для классов, которые не объявляют и не наследуют какие-либо виртуальные функции (и, следовательно, не имеют виртуальной таблицы);
  3. в некоторых случаях, связанных с виртуальными базовыми классами .

Понижающее преобразование с помощью static_cast

Оказывается, что понижающее преобразование также можно выполнить с помощью static_cast . Основное отличие состоит в том, что static_cast не выполняет проверку типов во время выполнения, чтобы убедиться, что то, что вы делаете, имеет смысл. Это делает использование static_cast более быстрым, но более опасным. Если вы приведете Base* к Derived* , преобразование будет «успешным», даже если указатель Base не указывает на объект Derived . Это приведет к неопределенному поведению, когда вы попытаетесь получить доступ к полученному указателю Derived (который на самом деле указывает на объект Base ).

Если вы абсолютно уверены, что указатель, для которого вы выполняете понижающее преобразование, будет корректным, использование static_cast допустимо. Один из способов убедиться, что вы знаете, на какой тип объекта вы указываете, – использовать виртуальную функцию. Вот один из способов (не самый лучший, потому что в нем используется глобальная переменная):

Но если вы готовы пройти через все эти проблемы, чтобы реализовать это (и заплатить за вызов виртуальной функции и обработку результата), вы можете просто использовать dynamic_cast .

dynamic_cast и ссылки

Хотя во всех приведенных выше примерах показано динамическое приведение указателей (что является более распространенным), dynamic_cast также может использоваться и со ссылками. Это работает аналогично тому, как dynamic_cast работает с указателями.

Поскольку C++ не имеет «нулевой ссылки», dynamic_cast не может возвращать нулевую ссылку в случае ошибки. Вместо этого, если dynamic_cast ссылки терпит неудачу, генерируется исключение типа std::bad_cast . Об исключениях мы поговорим позже в этой серии статей.

dynamic_cast против static_cast

Начинающие программисты иногда не понимают, когда использовать static_cast , а когда dynamic_cast . Ответ довольно прост: используйте static_cast , если только вы не выполняете понижающие преобразование, так как в этом случае dynamic_cast обычно лучше. Однако вам также следует подумать о том, чтобы полностью избежать приведения типов и просто использовать виртуальные функции.

Понижающее преобразование против виртуальных функций

Некоторые разработчики считают, что dynamic_cast – это зло и указывает на плохой дизайн класса. Эти программисты говорят, что вместо него вам следует использовать виртуальные функции.

В общем случае, использование виртуальной функции должно быть предпочтительнее понижающего преобразования. Однако бывают случаи, когда понижающее преобразование является лучшим выбором:

  • когда вы не можете изменить базовый класс для добавления виртуальной функции (например, потому что базовый класс является частью стандартной библиотеки);
  • когда вам нужен доступ к чему-то, специфичному для производного класса (например, функция доступа, которая существует только в производном классе);
  • когда добавлять виртуальную функцию к базовому классу не имеет смысла (например, в базовом классе нет подходящего значения для возврата). Использование чисто виртуальной функции может быть здесь допустимо, если вам не нужно создавать экземпляры базового класса.

Предупреждение о dynamic_cast и RTTI

Динамическая идентификация типа данных (RTTI, run-time type information) – это функция C++, которая предоставляет информацию о типе данных объекта во время выполнения. Эта возможность используется в dynamic_cast . Поскольку RTTI требует значительных затрат производительности, некоторые компиляторы для оптимизации позволяют отключать RTTI. Излишне говорить, что если вы это сделаете, dynamic_cast не будет работать правильно.

dynamic_cast conversion

Safely converts pointers and references to classes up, down, and sideways along the inheritance hierarchy.

Contents

[edit] Syntax

dynamic_cast< new-type >( expression )
new-type pointer to complete class type, reference to complete class type, or pointer to (optionally cv-qualified) void
expression lvalue (until C++11) glvalue (since C++11) of a complete class type if new-type is a reference, prvalue of a pointer to complete class type if new-type is a pointer.

If the cast is successful, dynamic_cast returns a value of type new-type . If the cast fails and new-type is a pointer type, it returns a null pointer of that type. If the cast fails and new-type is a reference type, it throws an exception that matches a handler of type std::bad_cast .

[edit] Explanation

For the convenience of description, » expression or the result is a reference to T » means that «it is a glvalue of type T «, which follows the convention of decltype .

Only the following conversions can be done with dynamic_cast , except when such conversions would cast away constness or volatility.

Похожие статьи