reinterpret_cast
Исходя из имени этого оператора, он вводится для узкой специализации: "переосмыслить" значение, т.е. представить его в другом виде. Его используют для приведение несовместных типов: «указатель к объекту», «указатель к указателю». Из живого примера 1:
Где же нам такое может понадобиться? Если не брать в пример какой-нибудь зловещий хакинг чисел с плавающей запятой на уровне битов, то в основном, при работе с сырой памятью. Например, при записи и чтении данных в файл:
Наверняка вы знаете, что числа с плавающей точкой совершенно иначе представляются в системе, в отличии от целочисленных значений в двоичном виде. Это позволяет нам оперировать ну ооочень большими и маленькими десятичные значениями, выходящими за рамки возможного для целочисленных типов.
Давайте попробуем увидеть, как расставлены биты в типе
Так,
Однако, с точки зрения поставленной задачи, именно оно нам и нужно — тип
#cppcore
Исходя из имени этого оператора, он вводится для узкой специализации: "переосмыслить" значение, т.е. представить его в другом виде. Его используют для приведение несовместных типов: «указатель к объекту», «указатель к указателю». Из живого примера 1:
double *pointer_f64 = new double(42);Приводя типы таким оператором, мы никак его не преобразуем с точки зрения памяти. Но теперь, обращаясь к тем же байтам, как к другому типу, с ними можно работать иначе.
int64_t *pointer_i64 = reinterpret_cast<int64_t *>(pointer_f64);
int64_t value_i64 = reinterpret_cast<int64_t> (pointer_i64);
int32_t *pointer_i32 = reinterpret_cast<int32_t *>(value_i64);
Где же нам такое может понадобиться? Если не брать в пример какой-нибудь зловещий хакинг чисел с плавающей запятой на уровне битов, то в основном, при работе с сырой памятью. Например, при записи и чтении данных в файл:
void save(const double &hp)
{
file.write(reinterpret_cast<const uint8_t*>(&hp), sizeof(hp));
}
void load(double &hp)
{
file.read(reinterpret_cast<uint8_t*>(&hp), sizeof(hp));
}
Наверняка вы знаете, что числа с плавающей точкой совершенно иначе представляются в системе, в отличии от целочисленных значений в двоичном виде. Это позволяет нам оперировать ну ооочень большими и маленькими десятичные значениями, выходящими за рамки возможного для целочисленных типов.
Давайте попробуем увидеть, как расставлены биты в типе
double
в живом примере 2. При решении этой задачи можно воспользоваться готовой стандартной структурой std::bitset(unsigned long long)
для распечатки битов. Однако, есть нюанс! Тип данных double
можно привести к соразмерному unsigned long long
двумя разными способами, которые дают совершенно разный результат.Так,
static_cast
отбрасывает дробную часть и преобразует к целочисленному значению, а reinterpret_cast
просто иначе интерпретирует расставленные биты. В следствие этого из 42.0
мы получаем не 42
, а какое-то другое и явно отличное от исходного число: 4631107791820423168
. Очень похоже на то, как мы иногда в шутку пытаемся услышать слова родного языка в иностранных песнях.Однако, с точки зрения поставленной задачи, именно оно нам и нужно — тип
unsigned long long
просто выступает в роли «грузового контейнера» для транспортировки 64 битов, которые std::bitset
потом благополучно печатает в консоль.#cppcore