Явное приведение dynamic_cast в С++, начало знакомства

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

  • Для работы dynamic_cast должна быть включена поддержка RTTI

RTTI — это такой механизм, с помощью которого определяются типы объектов во время работы программы. dynamic_cast является частью этого механизма. В некоторых компиляторах RTTI может быть отключено по умолчанию. В зависимости от компилятора включают эту поддержку по разному. Если RTTI отключено, то имеет смысл смотреть документацию по компилятору, где будет написан способ включения.

Простейший пример, которым можно продемонстрировать работу dynamic_cast, может выглядеть так:
Для удобства восприятия моих пояснений к имени класса я буду добавлять приставку Т, обозначающую слово "тип". В дальнейших пояснениях это поможет избежать возможной путанницы.

В приведённом выше коде используется приведение по иерархии сверху-вниз. От главного класса к его подчинённым классам. Функция foo принимает указатель на основной класс, объекты, которые в этом коде передаются функции, имеют тип производного класса: TTuna — потомок TFish и TCarp — потомок TFish. Внутри функции в некотором роде изолированный локальный мир, в этом изолированном мире неведомо, какой именно объект в него попадает, в этот мир попадает общее понятие — TFish. Чтобы обрабатывать конкретный вид TFish, нужно явно пояснить, какой вид обрабатывается: если это TCarp, то пояснить, что делать, если это TCarp, если это TTuna, то объяснить, что делать, если это TTuna. Этот способ объяснения — явное приведение типов. dynamic_cast всегда возвращает или результат успешного приведения, или результат неудачи. Если dynamic_cast использовался для приведения указателя, то в случае неудачи возвращается NULL. Если dynamic_cast использовался для приведения ссылки, то в случае неудачи возвращается исключение bad_cast. Это различие существует потому что согласно канонам природы ссылок, пустой ссылки существовать не должно, ссылка всегда на что-то должна указывать. В примере, показанном выше, применяются указатели, поэтому используется проверка указателя на NULL. Если указатель куда-то указывает, то выполняется условие. Вы можете убрать условия и увидеть, что будут лишние выполнения.

  • При использовании dynamic_cast всегда используйте проверку на успешность приведения
  • Старайтесь избегать любых приведений везде, где это возможно

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

Нисходящее приведение типов может быть небезопасным. Ниже будет показан пример, в котором используется небезопасное нисходящее приведение.

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

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

Имеется в виду указатель, в который присваивается адрес:
— если у указателя тип такой же, как у объекта, который даёт ориентир на приведение — то значит типы равны, а коли типы равны, то и приведение безопасно.
— указатель на основной тип можно безопасно использовать как указатель на подтип.

В книге Прата о нисходящем приведении написано так:
схема:

Приведение #1 — безопасно. Потому что оно заносит в указатель типа Magnificent адрес объекта типа Magnificent.
Приведение #2 — небезопасно.
Приведение #3 — безопасно. Потому что в нем указателю на базовый класс присваивается адрес производного объекта.

Почему #2 небезопасно? Потому что происходит некоторого рода конфуз. Программа будет ожидать объект базового типа, которым задействуются поля его потомка. В действительности же это не так. Вы можете ещё раз посмотреть пример со структурами, который был показан чуть выше.

  • Вопрос о безопасности приведения типа имеет больший приоритет, чем вопрос о конечном типе объекта, к которому ведёт указатель

Благодаря полиморфизму в С++ знать точный тип объекта обычно не нужно, при использовании полиморфизма достаточно использовать указатель или ссылку на базовый класс. Если для базового типа определена виртуальная версия метода, то можно использовать указатель на этот тип, чтобы вызывать методы любых его потомков.

Ниже приводится код, написанный в книге Прата.
В этом коде имеется функция GetOne, внутри которой создаётся указатель на объект случайно выбранного типа, возвращает этот указатель функция GetOne как указатель на Grand. Внутри функции main написан цикл, в котором в указатель pg, тип которого как раз Grand*, присваивается адрес, отдаваемый функцией GetOne. После того, как подобрался адрес объекта случайного типа, используется метод Say, который будет выполняться для нужного объекта. Метод Say присутствует в двух классах из трёх, в первородном классе этого метода нет, поэтому для первого класса вызываться метод Say в примере не будет, это достигается благодаря тому, что ипользуется dynamic_cast и его возможность проверки на успешность приведения. Писать можно много, но лучше проанализируйте код, он поможет вам увидеть, что dynamic_cast может быть полезен.

Источники:

Все комментарии на сайте проверяются, поэтому ваш комментарий может появиться не сразу. Для вставки кода в комментарий используйте теги: [php]ВАШ_КОД[/php]

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

Ваш e-mail не будет опубликован.

Поиск

 
     
Яндекс.Метрика

НАГРАДИ АВТОРА САЙТА
WEBMONEY
R375024497470
U251140483387
Z301246203264
E149319127674

Демотиватор наполовину пуст, наполовину полон

Выражаю свою признательность

  • Максиму очень признателен за указание на мои ошибки и неточности.
  • Sergio ===> за оказание помощи в исправлении моих ошибок
  • Gen ===> за правильное стремление помочь другим новичкам и выявления моих ошибок