Явное преобразование типов в С++
В языке C++ есть 5 видов операций явного преобразования типов:
- конвертация C-style;
- применение оператора static_cast;
- применение оператора const_cast;
- применение оператора dynamic_cast;
- применение оператора reinterpret_cast.
С-style преобразование
С-style приведения типов данных доступен в языке C++, но считается не самодостаточным. С-style приведения типов данных может быть использован для преобразования любого типа в любой другой тип, при этом неважно насколько это небезопасное преобразование, например, преобразование целого числа в указатель типа int
. Казалось бы, такое преобразование невозможно, однако компилятор выполнит это приведение. Какой получится результат компилятору не важно. Синтаксис приведения С-style:
double res = (double)13 / 2; // res == 6.5
В этом примере тип int приводится к типу double, чтобы избежать усечения результата из-за целочисленного деления.
Метод приведения типов С-style не делает проверки типов на совместимость, как это могут сделать static_cast
и dynamic_cast
.
static_cast преобразование
Оператор приведенияstatic_cast
применяется для неполиморфного приведения типов на этапе компиляции программы. Отличие static_cast
от С-style преобразования состоит в том, что данный оператор приведения может отслеживать недопустимые преобразования, такие как приведение указателя к значению или наоборот, а также приведение указателей и ссылок разных типов считается корректным только, если это приведение вверх или вниз по одной иерархии наследования классов, либо это указатель на void
. В случае фиксации отклонения от данных ограничений будет выдана ошибка при компиляции программы.
int x = 10; double d; int* p; d = static_cast<double>(x); p = static_cast<int*>(x); // ошибка
Рекомендуется пользоваться операцией static_cast
, нежели С-style приведением, потому что static_cast
ограничивает недопустимое приведение типов и, следовательно — безопаснее.
dynamic_cast преобразование
Оператор приведения dynamic_cast
применяется для полиморфного приведения типов на этапе выполнения программы. Класс считается полиморфным, если в нем есть хотя бы одна виртуальная функция. Если указатель, подлежащий приведению, ссылается на объект результирующего класса или объект класса производный от результирующего, то приведение считается успешным. То же самое для ссылок. Если приведение невозможно, то на этапе выполнения программы будет возвращен nullptr
, если приводятся указатели. Если приведение производится над ссылками, то будет сгенерировано исключение std::bad_cast
. Несмотря на то, что dynamic_cast
предназначен для приведения полиморфных типов по иерархии наследования, он может быть использован и для обычных неполиморфных типов вверх по иерархии. В этом случае ошибка будет получена на этапе компиляции. Оператор приведения dynamic_cast
приводить к указателю на void, но не может приводить указатель на void к другому типу.
struct A{ //Сделали полиморфным virtual void do_some(){}; }; struct B:A{ }; int main() { A a = new B(); B *b = dynamic_cast<B>(a); }
Чтобы безопасно пользоваться динамическим приведением, все вызовы dynamic_cast
должны быть обрамлены в блок try/catch
и/или после преобразования проверены в конструкции if
.
const_cast преобразование
Оператор приведения const_cast
удаляет или добавляет квалификаторы const
и volatile
с исходного типа данных (простые типы, пользовательские типы, указатели, ссылки). Например, был const int,
а после преобразования стал int
или наоборот.
Квалификаторы const
и volatile
называют cv-квалификаторы (cv-qualifiers). Данные квалификаторы указываются перед именами типов. Квалификатор const задает константность, т.е. защищает переменную от изменения. Квалификатор volatile говорит о том, что значение переменной может меняться без явного выполнения присваивания. Это обеспечивает защиту от оптимизации компилятором операций с данной переменной.
int *x = new int(10); const int *p; p = const_cast<const int*>(x); *p = 33; //Ошибка !!!
reinterpret_cast преобразование
Оператор приведения reinterpret_cast
используется для приведения несовместимых типов. Может приводить целое число к указателю, указатель к целому числу, указатель к указателю (это же касается и ссылок). Является функционально усеченным аналогом приведения типов в стиле языка С. Отличие состоит в том, что reinterpret_cast
не может снимать квалификаторы const
и volatile,
а также не может делать небезопасное приведение типов не через указатели, а напрямую по значению.
int a[] = { 1,2,3,4,5,6,7,8,9,10 }; auto out = *reinterpret_cast<int (*) [3][3]>(a); // out == {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}