Пространства имён
Варианты
Действия

Преобразование dynamic_cast

Материал из cppreference.com
< cpp‎ | language
 
 
Язык С++
Общие темы
Управление потоком
Операторы условного выполнения
Операторы итерации (циклы)
Операторы перехода
Функции
Объявление функции
Выражение лямбда-функции
Спецификатор inline
Спецификации динамических исключений(до C++17*)
Спецификатор noexcept(C++11)
Исключения
Пространства имён
Типы
Спецификаторы
decltype(C++11)
auto(C++11)
alignas(C++11)
Спецификаторы длительности хранения
Инициализация
Выражения
Альтернативные представления
Литералы
Логические - Целочисленные - С плавающей запятой
Символьные - Строковые - nullptr(C++11)
Определяемые пользователем(C++11)
Утилиты
Атрибуты(C++11)
Types
Объявление typedef
Объявление псевдонима типа(C++11)
Casts
Неявные преобразования - Явные преобразования
static_cast - dynamic_cast
const_cast - reinterpret_cast
Выделение памяти
Классы
Свойства функции класса
Специальные функции-элементы
Шаблоны
Разное
 
Выражения
Общие
Категории значений (lvalue, rvalue, xvalue)
Порядок оценки (точки последовательности)
Константные выражения
Потенциально оцениваемые выражения
Первичные выражения
Лямбда-выражения(C++11)
Литералы
Целочисленные литералы
Литералы с плавающей запятой
Логические литералы
Символьные литералы, включая управляющие последовательности
Строковые литералы
Литерал нулевого указателя(C++11)
Пользовательский литерал(C++11)
Операторы
a=b, a+=b, a-=b, a*=b, a/=b, a%=b, a&=b, a|=b, a^=b, a<<=b, a>>=b
++a, --a, a++, a--
+a, -a, a+b, a-b, a*b, a/b, a%b, ~a, a&b, a|b, a^b, a<<b, a>>b
a||b, a&&b, !a
a==b, a!=b, a<b, a>b, a<=b, a>=b, a<=>b(начиная с C++20)
a[b], *a, &a, a->b, a.b, a->*b, a.*b
a(...), a,b, a?b:c
выражение new
выражение delete
выражение throw
alignof
sizeof
sizeof...(C++11)
typeid
noexcept(C++11)
Выражения свёртки(C++17)
Альтернативные представления операторов
Приоритет и ассоциативность
Перегрузка операторов
Сравнение по умолчанию(C++20)
Преобразования
Неявные преобразования
Обычные арифметические преобразования
const_cast
static_cast
reinterpret_cast
dynamic_cast
Явные преобразования: (T)a, T(a), auto(a), auto{a}(начиная с C++23)
Пользовательское преобразование
 

Безопасно преобразует указатели и ссылки на классы вверх, вниз и вбок по иерархии наследования.

Содержание

[править]Синаксис

)}}
dynamic_cast<целевой-тип>(выражение)
целевой-тип указатель на полный тип класса, ссылка на полный тип класса или указатель на (необязательно cv-квалифицированный) void
выражениеlvalue(до C++11)glvalue(начиная с C++11) полного типа класса, если целевой-тип является ссылкой, prvalue указатель на полный тип класса, если целевой-тип это указатель.

Если приведение успешно, dynamic_cast возвращает значение типа целевой-тип. Если приведение не удаётся, а целевой-тип является типом указателя, возвращается нулевой указатель этого типа. Если приведение не удаётся, а целевой-тип является ссылочным типом, генерируется исключение, соответствующее обработчику типа std::bad_cast.

[править]Объяснение

Для удобства описания "выражение или результат являются ссылкой на T" означает, что "это glvalue типа T", которое следует за соглашением decltype.

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

1) Если тип выражения в точности соответствует целевому-типу или менее cv-квалифицированной версии целевого-типа, результатом будет значение выражения, с типом целевой-тип. (Другими словами, dynamic_cast можно использовать для добавления константности. Неявное преобразование и static_cast также могут выполнять это преобразование.)
2) Если значением выражения является значение нулевого указателя, результатом является значение нулевого указателя типа целевой-тип.
3) Если целевой-тип является указателем или ссылкой на Base, а тип выражения является указателем или ссылкой на Derived, где Base это уникальный доступный базовый класс для Derived, результатом является указатель или ссылка на подобъект класса Base в объекте Derived, указанный или идентифицированный выражением. (Примечание: неявное преобразование и static_cast также могут выполнять это преобразование.)
4) Если выражение является указателем на полиморфный тип, а целевой-тип является указателем на void, результатом является указатель на наиболее производный объект, на который указывает или на который ссылается выражение.
5) Если выражение является указателем или ссылкой на полиморфный типBase, а целевой-тип является указателем или ссылкой на тип Derived выполняется проверка во время выполнения:
a) Проверяется наиболее производный объект, указанный/идентифицированный выражением. Если в этом объекте выражение указывает/ссылается на общедоступный базовый класс Derived, и если только один объект типа Derived является производным от объекта, указанного/идентифицированного выражением, тогда результат приведения указывает/ссылается на этот объект Derived. (Это известно как "приведение низ".)
b) Иначе, если выражение указывает/ссылается на общедоступный базовый класс самого производного объекта, и одновременно самый производный объект имеет однозначный открытый базовый класс типа Derived, результат приведение указывает/ссылается на этот Derived (Это известно как "приведение в бок".)
c) Иначе проверка времени выполнения завершится неудачно. Если dynamic_cast используется для указателей, возвращается значение нулевого указателя типа целевой-тип. Если он использовался для ссылок, выдается исключение std::bad_cast.
6) Когда dynamic_cast используется в конструкторе или деструкторе (прямо или косвенно), а выражение ссылается на объект, который в данный момент находится в стадии построения/уничтожения, объект считается наиболее производным объектом. Если целевой-тип не является указателем или ссылкой на конструктор/деструктор собственного класса или одного из его базовых классов, поведение не определено.

Подобно другим выражениям приведения, результат будет следующим:

  • lvalue, если целевой-тип является ссылочным типом
  • rvalue, если целевой-тип является типом указателя
(до C++11)
  • lvalue, если целевой-тип является ссылочным типом lvalue, (выражение должно быть lvalue)
  • xvalue, если целевой-тип является ссылочным типом rvalue, (выражениеможет быть lvalue или rvalue(до C++17)должно быть glvalue (значения prvalue являются материализованными)(начиная с C++17) полного типа класса)
  • prvalue, если целевой-тип является типом указателя
(начиная с C++11)

[править]Примечание

  • Приведение вниз также можно выполнить с помощью static_cast, что позволяет избежать затрат на проверку во время выполнения, но безопасно только в том случае, если программа может гарантировать (с помощью какой-либо другой логики), что объект, на который указывает выражение, определённо является Derived.
  • Некоторые формы dynamic_cast полагаются на идентификацию типа во время выполнения (RTTI), то есть информацию о каждом полиморфном классе в скомпилированной программе. Компиляторы обычно имеют возможность отключить включение этой информации.

[править]Ключевые слова

dynamic_cast

[править]Пример

#include <iostream>   struct V {virtualvoid f(){}// должен быть полиморфным, чтобы использовать проверку// dynamic_cast во время выполнения};   struct A :virtual V {};   struct B :virtual V { B(V* v, A* a){// приведения во время создания (смотрите вызов в конструкторе D ниже)dynamic_cast<B*>(v);// чётко определено: v типа V*, V базовый для B, результатом// будет B*dynamic_cast<B*>(a);// неопределенное поведение: a имеет тип A*, A не является// базовым для B}};   struct D : A, B { D(): B(static_cast<A*>(this), this){}};   struct Base {virtual ~Base(){}};   struct Derived: Base {virtualvoid name(){}};   int main(){ D d;// самый производный объект A& a = d;// приведение вверх, dynamic_cast можно использовать, но это необязательно   [[maybe_unused]] D& new_d =dynamic_cast<D&>(a);// приведение вниз[[maybe_unused]] B& new_b =dynamic_cast<B&>(a);// приведение в бок   Base* b1 = new Base;if(Derived* d =dynamic_cast<Derived*>(b1); d != nullptr){std::cout<<"приведение вниз из b1 в d успешно\n"; d->name();// безопасный вызов}   Base* b2 = new Derived;if(Derived* d =dynamic_cast<Derived*>(b2); d != nullptr){std::cout<<"приведение вниз из b2 в d успешно\n"; d->name();// безопасный вызов}   delete b1; delete b2;}

Вывод:

приведение вниз из b2 в d успешно

[править]Отчёты о дефектах

Следующие изменения поведения были применены с обратной силой к ранее опубликованным стандартам C++:

Номер Применён Поведение в стандарте Корректное поведение
CWG 1269 C++11 проверка времени выполнения не выполнялась для xvalue выражений ,
если целевой-тип являлся ссылочным типом rvalue
выполняется

[править]Смотрите также

close