Преобразование dynamic_cast
Безопасно преобразует указатели и ссылки на классы вверх, вниз и вбок по иерархии наследования.
Содержание |
[править]Синаксис
dynamic_cast< целевой-тип>( выражение) | |||||||||
целевой-тип | — | указатель на полный тип класса, ссылка на полный тип класса или указатель на (необязательно cv-квалифицированный) void |
выражение | — | lvalue(до C++11)glvalue(начиная с C++11) полного типа класса, если целевой-тип является ссылкой, prvalue указатель на полный тип класса, если целевой-тип это указатель. |
Если приведение успешно, dynamic_cast возвращает значение типа целевой-тип. Если приведение не удаётся, а целевой-тип является типом указателя, возвращается нулевой указатель этого типа. Если приведение не удаётся, а целевой-тип является ссылочным типом, генерируется исключение, соответствующее обработчику типа std::bad_cast.
[править]Объяснение
Для удобства описания "выражение или результат являются ссылкой на T
" означает, что "это glvalue типа T
", которое следует за соглашением decltype
.
С помощью dynamic_cast, можно выполнять только следующие преобразования, за исключением случаев, когда такие преобразования отбрасывают константность или волатильность.
Base
, а тип выражения является указателем или ссылкой на Derived
, где Base
это уникальный доступный базовый класс для Derived
, результатом является указатель или ссылка на подобъект класса Base
в объекте Derived
, указанный или идентифицированный выражением. (Примечание: неявное преобразование и static_cast также могут выполнять это преобразование.)Base
, а целевой-тип является указателем или ссылкой на тип Derived
выполняется проверка во время выполнения:Derived
, и если только один объект типа Derived
является производным от объекта, указанного/идентифицированного выражением, тогда результат приведения указывает/ссылается на этот объект Derived
. (Это известно как "приведение низ".)Derived
, результат приведение указывает/ссылается на этот Derived
(Это известно как "приведение в бок".)Подобно другим выражениям приведения, результат будет следующим:
| (до C++11) |
| (начиная с C++11) |
[править]Примечание
- Приведение вниз также можно выполнить с помощью static_cast, что позволяет избежать затрат на проверку во время выполнения, но безопасно только в том случае, если программа может гарантировать (с помощью какой-либо другой логики), что объект, на который указывает выражение, определённо является
Derived
.
- Некоторые формы dynamic_cast полагаются на идентификацию типа во время выполнения (RTTI), то есть информацию о каждом полиморфном классе в скомпилированной программе. Компиляторы обычно имеют возможность отключить включение этой информации.
[править]Ключевые слова
[править]Пример
#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 | выполняется |