Чтение и запись бинарного файла с помощью reinterpreter_cast

Например, если у меня есть класс с именем Obj и класс Obj с именем obj1. Когда я хочу записать двоичный файл с помощью reinterpret_cast, я открываю этот файл в двоичном режиме, а затем

outstream.write( reinterpret_cast<const char *>(&obj1), sizeof(obj1) )

где outstream — это ifstream.

Когда я хочу прочитать из этого файла,

instream.read( reinterpret_cast<char *>(&obj1), sizeof(obj1) )

Что случилось. Он читает представление obj1 в двоичном формате и преобразует в obj1 или как это работает. Когда я пишу, я понимаю, что он интерпретирует obj1 одного байта, но когда я читаю, я не понимаю, что происходит.


person Otniel Mercea    schedule 19.09.2015    source источник
comment
Это не работает должным образом для сложных объектов. Вы должны искать де-/сериализацию.   -  person πάντα ῥεῖ    schedule 19.09.2015
comment
Если Ob не является типом POD, это поведение undefined. Используйте static_assert(std::is_pod<Obj>::value, "not POD");, чтобы убедиться в этом.   -  person 5gon12eder    schedule 19.09.2015
comment
Я не понимаю, как он расшифровывает информацию при чтении. Как я могу понять в чтении, что происходит за кулисами   -  person Otniel Mercea    schedule 19.09.2015
comment
За кулисами ничего не происходит, в этом и смысл reinterpret_cast! Кажется, вам нужно прочитать об указателях и различных типах приведения типов.   -  person Marcus Müller    schedule 19.09.2015
comment
Как мы уже говорили, для типов, отличных от POD, за кулисами происходит неопределенное поведение. И для типов POD это так же, как если бы вы memcpy биты объекта в / из файла.   -  person 5gon12eder    schedule 19.09.2015
comment
Я вижу, как это делается на С++. Как программировать Пятое издание, и они ничего не говорят, как вы. Я делаю это для файла произвольного доступа   -  person Otniel Mercea    schedule 19.09.2015
comment
Я не знаю эту книгу, поэтому не хочу судить о ней, но, возможно, это не совпадение, что ее нет на список.   -  person 5gon12eder    schedule 19.09.2015


Ответы (2)


Скажем, объект класса Obj занимает в памяти 4 байта.

[OBJ] = [0][1][2][3]
      = FF FF FF FF  //(for example, all bytes contain 0xFF)

char обычно занимает 1 байт. Поэтому, когда вы выполняете reinterpret_cast, ваш объект будет обрабатываться как 4 отдельных байта (так сказать, массив символов).

Когда вы сериализуете свой объект таким образом, вы будете хранить необработанные байты с 0 по 3, а ваш файл будет содержать 4 байтов 0xFF 0xFF 0xFF 0xFF. Суть в том, что вы копируете необработанные байты в файл.

Десериализация так же проста, у вас есть заполнитель Obj, который нужно прочитать, а затем те же байты в файле перезапишут объект.


Есть предостережение. Такая сериализация будет работать только с типами или структурами POD с простыми элементами данных. Так,

struct Foo {
    int bar1;
    float bar2;
};

Он не будет работать с такими вещами, как std::vector или std::list и т. д. В общем случае он не будет работать с указателями, потому что, если вы сохраните необработанное значение (адрес), которое содержит указатель, оно будет бессмысленным при следующем запуске вашей программы. и прочитайте этот адрес. Ваш объект будет содержать недопустимый указатель после десериализации.

person aslg    schedule 19.09.2015
comment
и если obj является pod, то при чтении он использует то же преобразование байтов char для преобразования в obj1, что и при записи для преобразования obj1 в char? - person Otniel Mercea; 19.09.2015
comment
Конверсии никогда не было, поэтому я сказал лечили. Повторное приведение не преобразует ваши данные. Данные (байты) одинаковы независимо от того, с каким типом вы имеете дело, что меняется в том, как компилятор использует эти данные. Когда вы переинтерпретируете что-то, вы говорите компилятору обрабатывать эти данные, как будто это что-то другое, но преобразования не происходит. - person aslg; 19.09.2015
comment
хорошо, теперь я начинаю понимать, что вы сказали. Итак, я понимаю, что вместо char будет любой базовый тип, и результат будет таким? - person Otniel Mercea; 19.09.2015
comment
Если вы имеете в виду, что можете использовать любой тип и переосмыслить его преобразование в char, я упомянул об этом в своем ответе. Если вы имеете в виду, что можете переинтерпретировать приведение к любому другому типу, кроме char, вы тоже можете это сделать, но на свой страх и риск. Не делайте этого, если не знаете, что делаете. - person aslg; 19.09.2015

Когда вы переинтерпретируете указатель на объект как указатель на char, вы записываете в память представление самого объекта. Это включает в себя

  • Все указатели внутри объекта. Для классов с виртуальными членами это может включать указатель на vtable, в зависимости от реализации.
  • Все отступы между членами,
  • Все члены данных, в соответствии с порядком байтов вашего оборудования.

Эта запись не включает какую-либо часть вашего объекта, на которую указывают указатели.

Более того, когда вы читаете объект с указателями обратно в память, значения в этих указателях будут мусором. Если вы разыменуете любой из них, вы получите неопределенное поведение.

Это делает метод применимым только к объектам простых старых данных (POD), то есть к примитивам и структурам/классам, состоящим из примитивов и других объектов POD. Кроме того, методика неприменима, когда нужна кросс-аппаратная совместимость.

person Sergey Kalinichenko    schedule 19.09.2015