Ответ
В C++ существует четыре основных вида явного приведения типов, каждый со своей семантикой и областью применения:
-
static_cast— базовое приведение, проверяемое на этапе компиляции. Используется для преобразований между числовыми типами, указателями вверх/вниз по иерархии наследования (без проверки на этапе выполнения) и для явного вызова конструкторов/операторов преобразования.double d = 3.14; int i = static_cast<int>(d); // Преобразование double в int Base* basePtr = new Derived(); Derived* derivedPtr = static_cast<Derived*>(basePtr); // Вниз по иерархии (опасно, если basePtr не указывает на Derived) -
dynamic_cast— предназначен для безопасного приведения вниз или вбок по иерархии полиморфных типов (классов с виртуальными функциями). Проверка осуществляется во время выполнения (RTTI). При неудаче для указателей возвращаетnullptr, для ссылок — исключениеstd::bad_cast.Base* b = new Derived(); Derived* d = dynamic_cast<Derived*>(b); // Успешно, если b действительно указывает на Derived if (d) { // Работаем с d } -
const_cast— используется для добавления или удаления квалификаторовconstиvolatile. Основное применение — работа с устаревшим кодом, где функция принимает неконстантный указатель, хотя данные не изменяются.void legacyFunction(char* str); const char* greeting = "Hello"; // legacyFunction(greeting); // Ошибка компиляции legacyFunction(const_cast<char*>(greeting)); // Опасно, если legacyFunction модифицирует данные -
reinterpret_cast— низкоуровневое приведение, которое интерпретирует битовое представление объекта одного типа как объект другого типа. Крайне опасно и зависит от платформы (порядок байт, выравнивание). Используется, например, для приведения указателя к целочисленному типу или между несвязанными типами указателей.int* ip = new int(0x41424344); char* cp = reinterpret_cast<char*>(ip); // Интерпретируем память int как массив char // cp[0] может быть 'D' (ASCII 0x44) или 'A' (0x41) в зависимости от endianness
C-style cast (type)value в C++ пытается применить комбинацию static_cast, const_cast и reinterpret_cast и может быть неявным. Его использование не рекомендуется, так как оно менее безопасно и затрудняет поиск мест приведения в коде.
Ответ 18+ 🔞
Э, слушай, давай разберём эту тему про касты в плюсах, а то тут, бля, овердохуища вариантов, и каждый норовит тебя подставить. Представь, что у тебя есть кувалда, скальпель, отвёртка и волшебная палочка, которая иногда взрывается. Вот примерно так и с этими приведениями.
Ну, первое — это static_cast. Это как твой надёжный, предсказуемый друг. Хочешь double в int запихнуть? Пожалуйста, но дробная часть, ясное дело, нахуй слетит. Нужно указатель на базовый класс скастовать к производному? Да без проблем, компилятор тебе поверит на слово! Но вот если ты обманешь и попробуешь кастовать не туда, куда надо, — получишь неопределённое поведение, и программа пойдёт в разнос, как мартышлюшка с гранатой. В общем, доверия ебать ноль, вся ответственность на тебе.
Base* basePtr = new Derived();
// Компилятор: "Окей, поверю. На, держи указатель."
Derived* derivedPtr = static_cast<Derived*>(basePtr);
// А если basePtr на самом деле на что-то другое указывал? Пиздец, Карл.
Дальше идёт dynamic_cast. Вот это уже хитрая жопа. Он не верит тебе на слово, а лезет в рантайм и проверяет, а туда ли ты вообще целишься. Работает только с полиморфными классами (у которых есть виртуальные функции), потому что ему нужно заглянуть в ту самую волшебную таблицу. Если каст прошёл успешно — красота. Если нет, и ты кастуешь указатель — получишь nullptr. А если ссылку — тебе в лицо прилетит std::bad_cast, и будет тебе хиросима и нигерсраки. Зато безопасно, мать его.
Base* b = new Derived();
Derived* d = dynamic_cast<Derived*>(b); // Проверяет в рантайме
if (d) { // Ура, всё ок!
// ...
} else { // Получил nullptr, нихуя не вышло
// ...
}
Третий в нашем параде — const_cast. Это такой полупидор, который умеет только одно: снимать или навешивать const и volatile. Казалось бы, зачем? Ну, например, чтобы совместиться со старым говнокодом, который принимает char*, а у тебя const char*. Но, чувак, если ты снимешь const и таки попробуешь изменить данные, которые менять нельзя, — это будет пиздопроебибна, undefined behavior чистой воды. Сам от себя охуеешь, когда программа упадёт в самом неожиданном месте.
void oldShittyFunction(char* str); // Объявлена где-то в каменном веке
const char* niceString = "Don't touch me";
// oldShittyFunction(niceString); // Не, компилятор ругается
oldShittyFunction(const_cast<char*>(niceString)); // "Ну ладно, проходи..."
// А внутри oldShittyFunction делает str[0] = 'X'; И всё, приехали.
И наконец, король всех ужасов — reinterpret_cast. Это хуй с горы, ёпта. Он вообще не преобразует значения, он просто берёт битовое представление одного типа и говорит: "А давай-ка будем считать, что это вот этот другой тип". Кастуешь int* к char*? Получи, распишись. А потом выяснится, что на твоей архитектуре little-endian, и все байты задом наперёд. Кастуешь указатель в uintptr_t? Может, прокатит. Кастуешь Foo* в Bar*, где эти типы никак не связаны? Ну, удачи тебе, храбрый пилигрим. Поведение зависит от платформы, компилятора, фазы луны. Использовать — только если ты точно, блядь, знаешь, что делаешь, и готов нести ответственность.
int* ip = new int(0x41424344);
char* cp = reinterpret_cast<char*>(ip); // "Эй, память, ты теперь не int, ты char!"
// Что лежит в cp[0]? Хуй его знает. На x86 это, скорее всего, 0x44 ('D').
А ещё есть этот старый дедушка — C-style cast: (int)3.14. Так вот, это — зло. В плюсах он пытается применить подряд static_cast, const_cast и reinterpret_cast, пока что-нибудь не сработает. Он неявный, небезопасный и в коде его хуй найдёшь. Серьёзно, выкинь эту привычку, как говно из тапка. Пиши явно, что хочешь: static_cast, dynamic_cast и так далее. Так и код понятнее, и ты сам не запутаешься, что же, ёперный театр, тут происходит.
Короче, выбор инструмента — на твоей совести. static_cast для обычных преобразований, dynamic_cast для безопасного полиморфизма, const_cast только в крайних случаях, а reinterpret_cast — это уже крик души, когда все другие варианты накрылись медным тазом. И да пребудет с тобой здравый смысл.