11

我试图了解序列化/反序列化如何在不使用库的情况下在 C++ 中工作。我从简单的对象开始,但是当反序列化一个向量时,我发现,如果没有先写下它的大小,我就无法获得向量。此外,我不知道应该选择哪种文件格式,因为如果在矢量大小之前存在数字,我将无法正确阅读。此外,我想用类和地图容器来做到这一点。我的任务是序列化/反序列化这样的对象:

PersonInfo
{
    unsigned int    age_;
    string name_;
    enum { undef, man, woman } sex_;
}

Person : PersonInfo 
{
    vector<Person>      children_;
    map<string, PersonInfo>     addrBook_;
}

目前我知道如何序列化这样的简单对象:

vector<PersonInfo> vecPersonInfo;
vecPersonInfo.push_back(*personInfo);
vecPersonInfo.push_back(*oneMorePersonInfo);

ofstream file("file", ios::out | ios::binary);
if (!file) {
    cout<<"can not open file";
} else {
    vector<PersonInfo>::const_iterator iterator = vecPersonInfo.begin();
    for (; iterator != vecPersonInfo.end(); iterator++) {
        file<<*iterator;
    }

您能否建议,我该如何为这个复杂的对象执行此操作,或者一个可以清楚解释它的好教程?

4

2 回答 2

14

一种模式是实现一个定义序列化函数的抽象类,该类定义进入序列化器的内容和输出的内容。一个例子是:

class Serializable
{
public:
    Serializable(){}
    virtual ~Serializable(){}

    virtual void serialize(std::ostream& stream) = 0;
    virtual void deserialize(std::istream& stream) = 0;
};

然后为要序列化的类/结构实现 Serializable 接口:

struct PersonInfo : public Serializable // Yes! It's possible
{
    unsigned int age_;
    string name_;
    enum { undef, man, woman } sex_;

    virtual void serialize(std::ostream& stream)
    {
        // Serialization code
        stream << age_ << name_ << sex_;
    }

    virtual void deserialize(std::istream& stream)
    {
        // Deserialization code
        stream >> age_ >> name_ >> sex_;
    }
};

休息我相信你知道的。这里有一些障碍需要通过,您可以在闲暇时完成:

  1. 当您将一个字符串写入带有空格的流并尝试将其读回时,您将只得到其中的一部分,而字符串的其余部分会“破坏”之后读取的值。
  2. 您如何对其进行编程以使其跨平台(小端与大端)
  3. 您的程序如何自动检测反序列化时要创建的类。

线索:

  1. 使用具有编写 bool、int、float、strings 等功能的自定义序列化程序。
  2. 使用字符串表示正在序列化的对象类型,并在反序列化时使用工厂创建该对象的实例。
  3. 使用预定义的宏来确定您的代码正在编译的平台。
  4. 始终以固定的字节序写入文件,并使使用其他字节序的平台进行调整。
于 2012-07-10T15:12:24.267 回答
2

最基本的形式是定义一个定义虚拟读/写方法的“Serialisable”接口(抽象类)。您还定义了一个“流”接口,该接口为基本原始类型(例如读取/写入整数、浮点数、字节、字符、查找/重置)以及可能为某些复合类型(值数组,例如字符串、向量)提供通用 API等)在流上运行。如果适合您,您可以使用 C++ IOStreams。

您还需要一些 id 系统,以便工厂在加载/反序列化时创建相应的类,并在序列化复杂类型时进行引用,以便在必要时使用适当的结构/长度信息标记/标头每个逻辑部分。

然后,您可以为每种媒体(如文本文件、二进制文件、内存中、网络等)创建具体的流类。

然后,您希望可序列化的每个类都必须继承 Serializable 接口并实现细节(如果是复合/复杂类,则递归利用为其他类型定义的可序列化接口)。

这当然是一种添加序列化的幼稚且“侵入性”的方式(您必须修改参与的类)。然后,您可以使用模板或预处理器技巧来减少干扰。请参阅 Boost 或协议缓冲区,或任何其他库,以了解其在代码中的外观。

你真的确定要自己动手吗?它可能会变得非常混乱,尤其是当您有指针、对象之间的指针(包括循环)时,您还需要在加载/反序列化对当前运行正确之前的某个时间点修复/翻译这些指针。

于 2012-07-10T15:12:53.647 回答