Вот как должны читаться ваши вещи:
#include <fstream>
#include <string>
#include <stdexcept>
struct SerializeTestStruct
{
char mCharVal;
unsigned int mIntVal;
void Serialize(::std::ostream &os);
static SerializeTestStruct Deserialize(::std::istream &is);
};
void SerializeTestStruct::Serialize(std::ostream &os)
{
if (os.good())
{
os.write((char*)&mCharVal, sizeof(mCharVal));
os.write((char*)&mIntVal, sizeof(mIntVal));
}
}
SerializeTestStruct SerializeTestStruct::Deserialize(std::istream &is)
{
SerializeTestStruct retval;
if (is.good())
{
is.read((char*)&retval.mCharVal, sizeof(retval.mCharVal));
is.read((char*)&retval.mIntVal, sizeof(retval.mIntVal));
}
if (is.fail()) {
throw ::std::runtime_error("failed to read full struct");
}
return retval;
}
int main(int argc, const char *argv[])
{
//ultra basic serialization test.
// setup
const ::std::string testFileName = "test.bin";
// write
{
SerializeTestStruct testStruct;
testStruct.mCharVal = 'y';
testStruct.mIntVal = 9;
::std::ofstream fileOut(testFileName.c_str());
fileOut.open(testFileName.c_str(),
std::ofstream::binary|std::ofstream::out);
fileOut.clear();
testStruct.Serialize(fileOut);
}
// read
{
::std::ifstream fileIn (testFileName.c_str(),
std::ifstream::in|std::ifstream::binary);
if (fileIn.is_open())
{
SerializeTestStruct testStruct = \
SerializeTestStruct::Deserialize(fileIn);
::std::cout << "testStruct.mCharVal == '" << testStruct.mCharVal
<< "' && testStruct.mIntVal == " << testStruct.mIntVal
<< '\n';
}
}
return 0;
}
Проблемы стиля:
- Не используйте
new
для создания вещей, если вы можете помочь.Выделенные в стек объекты обычно являются тем, что вы хотите, и ими значительно легче управлять, чем произвольными объектами времени жизни, которые вы выделяете из кучи.Если вы используете new
, рассмотрите возможность использования какого-либо типа интеллектуального указателя, чтобы помочь вам управлять временем жизни. - Код сериализации и десериализации должен быть сопоставлен, чтобы их можно было изучить и изменить вместе.Это делает обслуживание такого кода намного проще.
- Положитесь на C ++, чтобы очистить вас от деструкторов, вот для чего они.Это означает создание базовых блоков, содержащих части вашего кода, если область используемых переменных относительно ограничена.
- Не используйте ненужные флаги.
Ошибки ...
- Не используйте функцию
data
члена ::std::string
. - Использование размещения
new
, и этот блок памяти - действительно плохая идея, потому что это невероятно сложно.И если вы использовали его, то вы не используете удаление массива так, как вы это делали.И, наконец, он все равно не будет работать по причине, объясненной позже. - Не используйте
ofstream
в типе, принятом вашей функцией Serialize
, поскольку это производный класс, функции которого вам не нужны,Вы всегда должны использовать самый базовый класс в иерархии, который имеет необходимые вам функции, если только у вас нет особых причин не делать этого.Serialize
отлично работает с функциями базового класса ostream
, поэтому используйте этот тип вместо этого. - Расположение вашей структуры на диске и расположение в памяти не совпадают, так что ваша техника размещения - новая техникаобречен на провал.Как правило, если у вас есть функция
serialize
, вам нужна соответствующая функция deserialize
.
Вот еще одно объяснение вашей проблемы с макетом памяти.Структура в памяти для Linux-бокса на базе x86_64 выглядит следующим образом:
+------------+-----------+
|Byte number | contents |
+============+===========+
| 0 | 0x79 |
| | (aka 'y') |
+------------+-----------+
| 1 | padding |
+------------+-----------+
| 3 | padding |
+------------+-----------+
| 4 | padding |
+------------+-----------+
| 5 | 9 |
+------------+-----------+
| 6 | 0 |
+------------+-----------+
| 7 | 0 |
+------------+-----------+
| 8 | 0 |
+------------+-----------+
Содержимое раздела padding
не определено, но обычно 0
.Это не имеет значения, потому что это пространство никогда не используется и просто существует, так что доступ к следующему int
лежит на эффективной 4-байтовой границе.
Размер вашей структуры на диске составляет 5 байтов,и полностью отсутствует разделы заполнения.Таким образом, это означает, что когда вы читаете его в память, он вообще не согласуется со структурой в памяти, и доступ к нему может вызвать какую-то ужасную проблему.
Первое правило, если вам нужноserialize
функция, вам нужна deserialize
функция.Второе правило: если вы точно не знаете, что делаете, не записывайте необработанную память в файл.Это будет хорошо работать во многих случаях, но есть важные случаи, в которых это не будет работать.И если вы не знаете, что работает и не работает, и когда это работает или не работает, вы в конечном итоге получите код, который, кажется, работает нормально в определенных тестовых ситуациях, но терпит неудачу, когда вы пытаетесь использовать его вреальная система.
Мой код все еще делает дамп памяти в файл.И это должно работать до тех пор, пока вы читаете результат обратно на той же самой архитектуре и платформе с кодом, скомпилированным с той же версией компилятора, что и при написании.Как только одна из этих переменных изменится, все ставки отключены.