что такое dynamic cast c это

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

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

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

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

dynamic_cast

Сбой dynamic_cast

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

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

Правило

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

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

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

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

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

dynamic_cast и ссылки

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

dynamic_cast против static_cast

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

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

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

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

Источник

Приведение типов. Наглядное отличие static_cast от dynamic_cast

Доброго времени суток. Очень много статей в интернете о разнице операторов приведения типов, но понимания в данной теме они мне не особо то и не добавили. Пришлось разбираться самому. Хочу поделиться с вами моим опытом на довольно наглядном примере.

Статья рассчитана на тех, кто хочет осознать приведение типов в С++.

Итак, пусть у нас есть такая иерархия наследования:

На картинке изображена иерархия наследования и расположение членов-данных наследников в памяти

что такое dynamic cast c это. Смотреть фото что такое dynamic cast c это. Смотреть картинку что такое dynamic cast c это. Картинка про что такое dynamic cast c это. Фото что такое dynamic cast c это

Небольшое отступление: почему так важно преобразование типов? Говоря по рабоче-крестьянски, при присваивании объекту типа X объект типа Y, мы должны определить, какое значение будет иметь после присваивания объект типа X.

Начнем с использования static_cast:

Почему таков эффект при выводе значений указателей (значение указателя это адрес, по которому лежит переменная)? Дело в том, что static_cast производит сдвиг указателя.
Рассмотрим на примере:

1. Происходит преобразование типа из C* в D*. Результатом этого есть указатель типа D* (назовем его tempD), который указывает (внимание!) на ту часть в объекте класса C, которая унаследована от класса D. Значение самого pC не меняется!

2. Теперь присваиваем указателю pD значение указателя tempD (всё хорошо, типы одинаковы)
Разумный вопрос: а зачем собственно нужно сдвигать указатель? Говоря по простому, указатель класса D* руководствуется определением класса D. Если бы не произошло смещения, то меняя значения переменных через указатель D, мы бы меняли переменные объекта класса С, которые не относятся к переменным, унаследованным от класса D (если бы указатель pD имел то же значение, что и pC, то при обращении pD->f в действительности мы бы работали с переменной
а).

Промежуточный итог: static_cast при работе с иерархией классов определяет значения указателей так, чтобы обращение к переменным класса через указатель было корректным.

Поговорим о недостатках static_cast. Вернемся к той же иерархии наследования.

Рассмотрим такой код:

Почему pC->f имеет значение отличное от 0? Рассмотрим код по строчкам:

что такое dynamic cast c это. Смотреть фото что такое dynamic cast c это. Смотреть картинку что такое dynamic cast c это. Картинка про что такое dynamic cast c это. Фото что такое dynamic cast c это

Теперь если мы хотим сделать запись в переменную g через указатель pB (ведь pB полностью уверен что указывает на объект типа B), мы на самом деле запишем данные в переменную f, унаследованную от класса D. Причем указатель pD будет интерпретировать информацию, записанную в переменную f, как float, что мы и видим при выводе через cout.

Как решить такую проблему?
Для этого следует использовать dynamic_cast, который проверяет не только валидность иерархии классов, но и тот факт, что указатель действительно указывает на объект того типа, к которому мы хотим привести.

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

Демонстрация решения проблемы, при той же иерархии классов:

Предлагаю запустить код и убедиться, что операция

не получится (потому что pA указывает на объект типа С, что и проверил dynamic_cast и вынес свой вердикт).

Ссылок никаких не привожу, источник — личный опыт.

Источник

Еще раз про приведение типов в языке С++ или расстановка всех точек над cast

что такое dynamic cast c это. Смотреть фото что такое dynamic cast c это. Смотреть картинку что такое dynamic cast c это. Картинка про что такое dynamic cast c это. Фото что такое dynamic cast c это

Этот пост попытка кратко оформить все, что я читал или слышал из разных источников про операторы приведения типов в языке C++. Информация ориентирована в основном на тех, кто изучает C++ относительно недолго и, как мне кажется, должна помочь понять cпецифику применения данных операторов. Старожилы и гуру С++ возможно помогут дополнить или скорректировать описанную мной картину. Всех интересующихся приглашаю под кат.

Приведение типов в стиле языка C (C-style cast)

Приведение типов в стиле языка C может привести выражение любого типа к любому другому типу данных (исключение это приведение пользовательских типов по значению, если не определены правила их приведения, а также приведение вещественного типа к указателю или наоборот). К примеру, unsigned int может быть преобразован к указателю на double. Данный метод приведения типов может быть использован в языке C++. Однако, метод приведения типов в стиле языка C не делает проверки типов на совместимость, как это могут сделать static_cast и dynamic_cast на этапе компиляции и на этапе выполнения соответственно. При этом все, что умеют const_cast и reinterpret_cast данный метод приведения типов делать может.

Общий вид приведения:

, где new_type – новый тип, к которому приводим, а exp – выражение, которое приводится к новому типу.

Т.к. данный оператор не имеет зарезервированного ключевого слова (например, static_cast) найти все места приведения типов в тексте программы будет не очень удобно, если это потребуется.

const_cast

Оператор приведения const_cast удаляет или добавляет квалификаторы const и volatile с исходного типа данных (простые типы, пользовательские типы, указатели, ссылки). Например, был const int, а после преобразования стал int или наоборот. Квалификаторы const и volatile называют cv-квалификаторы (cv-qualifiers). Данные квалификаторы указываются перед именами типов. Как ни трудно догадаться квалификатор const задает константность, т.е. защищает переменную от изменения. Квалификатор volatile говорит о том, что значение переменной может меняться без явного выполнения присваивания. Это обеспечивает защиту от оптимизации компилятором операций с данной переменной.

Общий вид приведения:

Дополнительный пример от пользователя 5nw

reinterpret_cast

Оператор приведения reinterpret_cast используется для приведения несовместимых типов. Может приводить целое число к указателю, указатель к целому числу, указатель к указателю (это же касается и ссылок). Является функционально усеченным аналогом приведения типов в стиле языка С. Отличие состоит в том, что reinterpret_cast не может снимать квалификаторы const и volatile, а также не может делать небезопасное приведение типов не через указатели, а напрямую по значению. Например, переменную типа int к переменной типа double привести при помощи reinterpret_cast нельзя.

Общий вид приведения:

static_cast

Оператор приведения static_cast применяется для неполиморфного приведения типов на этапе компиляции программы. Отличие static_cast от приведения типов в стиле языка C состоит в том, что данный оператор приведения может отслеживать недопустимые преобразования, такие как приведение указателя к значению или наоборот (unsigned int к указателю на double не приведет), а также приведение указателей и ссылок разных типов считается корректным только, если это приведение вверх или вниз по одной иерархии наследования классов, либо это указатель на void. В случае фиксации отклонения от данных ограничений будет выдана ошибка при компиляции программы. При множественном наследовании static_cast может вернуть указатель не на исходный объект, а на его подобъект.

Общий вид приведения:

dynamic_cast

Оператор приведения dynamic_cast применяется для полиморфного приведения типов на этапе выполнения программы (класс считается полиморфным, если в нем есть хотя бы одна виртуальная функция). Если указатель, подлежащий приведению, ссылается на объект результирующего класса или объект класса производный от результирующего то приведение считается успешным. То же самое для ссылок. Если приведение невозможно, то на этапе выполнения программы будет возвращен NULL, если приводятся указатели. Если приведение производится над ссылками, то будет сгенерировано исключение std::bad_cast. Несмотря на то, что dynamic_cast предназначен для приведения полиморфных типов по иерархии наследования, он может быть использован и для обычных неполиморфных типов вверх по иерахии. В этом случае ошибка будет получена на этапе компиляции. Оператор приведения dynamic_cast приводить к указателю на void, но не может приводить указатель на void к другому типу. Способность dynamic_cast приводить полиморфные типы обеспечивается системой RTTI (Run-Time Type Identification), которая позволяет идентифицировать тип объекта в процессе выполнения программы. При множественном наследовании dynamic_cast может вернуть указатель не на исходный объект, а на его подобъект.

Источник

Приведение типов

Будучи на конференции Qt Developer Days 2010 я узнал, что одним из самых популярных вопросов на собеседовании в разные зарубежные компании, работающие с Qt библиотекой, является вопрос о различиях в способах приведения типов в C++. Поэтому здесь я рассмотрю основные различия между static_cast, dynamic_cast, const_cast, reinterpret_cast, C-style cast, qobject_cast и qvariant_cast

static_cast преобразует выражения одного статического типа в объекты и значения другого статического типа. Поддерживается преобразование численных типов, указателей и ссылок по иерархии наследования как вверх, так и вниз. Проверка производится на уровне компиляции, так что в случае ошибки сообщение будет получено в момент сборки приложения или библиотеки.

Используется для динамического приведения типов во время выполнения. В случае неправильного приведения типов для ссылок вызывается исключительная ситуация std::bad_cast, а для указателей будет возвращен 0. Использует систему RTTI (Runtime Type Information). Безопасное приведение типов по иерархии наследования, в том числе для виртуального наследования.

Пожалуй самое простое приведение типов. Снимает cv qualifiers — const и volatile, то есть константность и отказ от оптимизации компилятором переменной. Это преобразование проверяется на уровне компиляции и в случае ошибки приведения типов будет выдано сообщение.

Приведение типов без проверки. reinterpret_cast — непосредственное указание компилятору. Применяется только в случае полной уверенности программиста в собственных действиях. Не снимает константность и volatile. применяется для приведения указателя к указателю, указателя к целому и наоборот.

Си-шный метод приведения типов. Пожалуй самый нежелательный способ приведения типов. Страуструп пишет:
«Например, что это значит выражение — x = (T)y;. Мы не знаем. Это зависит от типа T, типов x и y. T может быть названием типа, typedef или может быть параметр template-а. Может быть, х и у являются скалярными переменными и Т представляет собой значение преобразования. Может быть, х объекта класса, производного от класса Y и Т — нисходящее преобразование. По этой причине программист может не знать, что он делает на самом деле.»
Вторая причина нежелательного использования приведения типов в C-style — трудоемкость процесса поиска мест приведения типов.

Приводит объект QObject* к типу TYPE если объект типа объекта TYPE или тип наследует от TYPE иначе возвращает 0. qobject_cast от 0 также дает 0. Необходимое условие. Класс должен наследовать от QObject и содержать в себе макрос Q_OBJECT. Функция ведет себя аналогично стандартному dynamic_cast, но при этом не использует RTTI. Вот как описана данная функция в Qt 4.7.0:

Итак, что тут происходит:

Во-первых если не определены QT_NO_MEMBER_TEMPLATES (определяется только в том случае, если используется версия Microsoft Visual Studio ниже 2002) и QT_NO_QOBJECT_CHECK (определяется в случае использования версии Microsoft Visual Studio ниже 2003), то происходит проверка наличия макроса Q_OBJECT в объявлении класса. И после этого выполняется непосредственно само преобразование — сначала получаем статический объект класса QMetaObject, который называется staticMetaObject, у которого вызывается метод cast, который возвращает const_cast переданного ему объекта, попутно проверяя наследуется ли данный объект от QObject. Далее полученному объекту делается static_cast и возвращается результат.

Приводит объект класса QVariant к нужному классу. Функция аналогична функции qVariantValue.

Рассмотрим, что происходит внутри:

В первой секции кода производится получение идентификатора класса через метасистему Qt. В том случае если класс не зарегистрирован через Q_DECLARE_METATYPE, компиляция кода с приведением к этому типу выдаст ошибку. Далее, если тип объекта, полученный от метасистемы совпадает с типом в значении QVariant, производится reinterpret_cast содержимого объекта, если идентификатор класса не является встроенным типом и его id не совпадает с заложенным в значении QVariant, то возвращается TYPE(). Для случаев, когда мы приводим к встроенному типу, вызывается функция qvariant_cast_helper, которая вызывает в свою очередь функцию convert, адрес которой хранится в структуре Handler. В ней уже осуществляется приведение способом подходящим для типа TYPE. Если конвертация не удалась возвращается объект TYPE()

UPD: Спасибо BaJlepa:
1. const_cast также умеет добавлять cv-квалификаторы
2. для преобразования указателей лучше использовать двойной static_cast через void* вместо reinterpret_cast, потому как такое преобразование позволяет быть уверенным в том, что только pointer-ы участвуют в приведении

Источник

Оператор dynamic_cast

Синтаксис

Remarks

Параметр type-id должен быть указателем или ссылкой на ранее определенный тип класса или «указателем на void». Тип операнда expression должен быть указателем, если type-id является указателем, или l-значением, если type-id является ссылкой.

Существует два критических изменения в работе dynamic_cast управляемого кода:

dynamic_cast на указатель к базовому типу упакованного перечисления вызывает сбой во время выполнения; вместо преобразованного указателя будет возвращено значение 0.

Этот тип преобразования называется «восходящим приведением типа», поскольку при нем указатель перемещается вверх по иерархии классов: от производного класса к классу, от которого он является производным. Восходящее приведение типа является неявным преобразованием.

Этот тип преобразования называется «нисходящим приведением типа», поскольку при нем указатель перемещается вниз по иерархии классов: от заданного класса к производному от него классу.

В случаях множественного наследования возникают возможности для неоднозначности. Рассмотрим для примера иерархию классов, показанную на следующем рисунке.

Для типов CLR dynamic_cast результатом является отсутствие операции, если преобразование может быть выполнено неявно, или isinst инструкция MSIL, выполняющая динамическую проверку и возвращающая nullptr в случае сбоя преобразования.

что такое dynamic cast c это. Смотреть фото что такое dynamic cast c это. Смотреть картинку что такое dynamic cast c это. Картинка про что такое dynamic cast c это. Фото что такое dynamic cast c это
Иерархия классов, показывающая множественное наследование

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

что такое dynamic cast c это. Смотреть фото что такое dynamic cast c это. Смотреть картинку что такое dynamic cast c это. Картинка про что такое dynamic cast c это. Фото что такое dynamic cast c это
Иерархия классов, показывающая виртуальные базовые классы

Рассмотрим для примера иерархию классов, показанную на следующем рисунке.

что такое dynamic cast c это. Смотреть фото что такое dynamic cast c это. Смотреть картинку что такое dynamic cast c это. Картинка про что такое dynamic cast c это. Фото что такое dynamic cast c это
Иерархия классов, показывающая повторяющиеся базовые классы

Значением приведения к типу указателя, которое привело к сбою, является пустой указатель. Неудачное приведение к ссылочному типу вызывает исключение bad_cast. Если не expression указывает на допустимый объект или ссылается на него, __non_rtti_object возникает исключение.

Пример

В следующем примере создается указатель базового класса (структура A) на объект (структура C). Это (а также тот факт, что они являются виртуальными функциями) порождает полиморфизм времени выполнения.

В этом примере также вызывается невиртуальная функция в иерархии.

Источник

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *