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