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

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

Материал из cppreference.com
< cpp‎ | language
 
 
Язык С++
Общие темы
Управление потоком
Операторы условного выполнения
if
Операторы итерации (циклы)
Операторы перехода
Функции
Объявление функции
Выражение лямбда-функции
Спецификатор 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
Выделение памяти
Классы
Свойства функции класса
explicit (C++11)
static
Специальные функции-элементы
Шаблоны
Разное
 
Выражения
Общие
Категории значений (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
{
    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
выполняется

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