C++中的简单序列化和反序列化

Simple serialization and deserialization in C++

本文关键字:反序列化 序列化 简单 C++      更新时间:2023-10-16

我需要一种方法来序列化不同类型的对象(但派生自同一类的类型),然后将它们反序列化为包含派生类的基类的指针。例如:

#include<iostream>
#include<fstream>
class One
{
public:
    int a;
    virtual void Func()
    {
    }
};
class One1: public One
{
    char s[128];
    void Func1(int l)
    {
        std::cout<<l<<'n';
    }
    void Func()
    {
         Func1(156);
    }
};
int main()
{
    One* x = new One1;
    x->Func();
    char* y=(char*)x;
    delete x;
    /*std::ofstream out("test11.txt",std::ofstream::out | std::ofstream::trunc);
    out.write(y,sizeof(One1));
    out.close();
    std::ifstream in("test11.txt",std::ifstream::in);
    char* y1=new char[sizeof(One1)];
    in.read(y1,sizeof(One1));*/
    One* z=(One*)y/*1*/;
    z->Func();
    return 0;
}

此代码输出

156
156

但是当我取消注释注释时(当我尝试将对象的 char 表示形式写入文件并从该文件读取时),程序输出156并在尝试执行z->Func();时因分段错误而结束。我检查了变量y的内容是否与y1不同。为什么?
该问题的原因是什么,我该如何解决它(也许通过使用一些特殊的库)?

1. 不要通过简单地复制字节来序列化派生类

您不能简单地通过将多态对象转换为字节数组,然后通过二进制read加载它们来编写它们。具有虚函数的类将指向实现的指针存储在 vtable 中。转储派生类的实例将导致转储存储在 vtable 中的指针,在再次运行程序后,该指针不一定是有效的指针。之后访问它很可能会产生分段错误。

如果您确实想使用简单的方法(直接读取和写入字节),请使用 POD 类。

2. 不要访问无效指针

虽然以上是答案中最重要的部分(因为它会完全改变你的程序),但还有其他需要强调的事情。命令:

char* y=(char*)x;

创建指向 x 地址的char指针。不会复制对象。因此,当您稍后执行以下操作时:

delete x;

指向y的指针变为无效。当您稍后尝试将其写入文件时:

std::ofstream out("test11.txt",std::ofstream::out | std::ofstream::trunc);
out.write(y,sizeof(One1));

您访问不属于您的内存。至少现在不是了,因为你明确表示你不需要它,通过早点打电话delete