在C ++中保存复杂对象的方法

时间:2012-03-07 14:58:18

标签: c++ object serialization save

我总是通过将ASCII写入文件来保存数据,即

param1 = value1
param2 = string string string

并加载了令人烦恼的解析开销。我只是通过将整个对象写入二进制文件来尝试编写我的编程游戏,la

class Record {
    int par1;
    string par2;
    vector<string> par3;
    void saveRecord(string fName);
    void loadRecord(string fName);
}

Record::saveRecord() {
    ...
    fstream outFile(fName.c_str(), fstream::out | fstream::binary);
    outFile.write( (char*)this, sizeof(Record) );
    outFile.close();
}

等等。但我发现这不起作用,因为复杂的数据类型(例如字符串,向量)涉及指针,其值不能以这种方式存储。

所以听起来像选项是

A)编写复杂的序列化算法,将每个复杂的数据类型转换为基元,然后保存为二进制;或

B)根据我的初始策略,只需将所有内容写入ASCII文件。

第一种方法似乎过于复杂,而第二种方法则非常优雅。

还有其他选择吗? 是否有标准程序?

注意:我已经看过boost :: serialization库,它看起来也非常不优雅而且非常繁琐 - 也就是说,我只想编写自己的序列化方法,如果这是正确的方法论。

4 个答案:

答案 0 :(得分:3)

没有。使用Boost.Serialization或Google Protocol Buffers。是的,您必须编写将数据放入序列化容器或从序列化容器中提取数据的函数。这就是预期实际可行的强大解决方案的完成方式。

通过这种方式,您可以获得二进制文件的版本控制,兼容性和可移植性。如果将数据视为一堆字节,并且写入/读取所有内容,则在更改结构时,或者从具有不同填充/字节大小/字节顺序的构建中写入文件时,将无法读取旧文件

它可能适用于简单的东西,但会破坏得如此之快,你会后悔没有从一开始就做到这一点。

答案 1 :(得分:2)

您提到的两种策略

A)编写复杂的序列化算法,将每个复杂的数据类型转换为基元,然后保存为二进制;或

B)根据我的初始策略,将所有内容写入ASCII文件。

它通常是如何完成的。您实际上是在创建自己的文件格式。最常见的范例是块范例。保存对象或对象集时,首先要编写一个表示对象大小或数据“块”的int。而下一个int表示对象的种类。如果您关心支持用户升级其软件时保存的配置,您可能还需要包含版本信息。

当您关心数据非常精确时,选项A非常有用,并且可以更容易地在c ++中加载/保存。例如。以这种方式保存的浮点数将加载与保存的完全相同的值。

当您想要查看要保存的内容时,选项B非常有用,并且可能是某人手动修改数据。保存在这里的浮动,当加载回来时将不完全相同。

尝试查看其他文件格式以获取示例。 Midi文件格式使用块范例,并且还具有流功能,使用选项A.Wavefront“obj”文件格式用于3D应用程序,因为它的简单性使用选项B.所有内容都可以在您喜欢的文本编辑器中读取。

答案 2 :(得分:1)

如果您想坚持使用基于文本的序列化,您可以尝试覆盖:

  • std::ostream& operator <<(std::ostream& os, const Type& obj);用于序列化,
  • std::istream& operator >>(std::istream& is, Type& obj);用于反序列化。

库已经对原始类型进行了序列化和反序列化,您不需要访问类或模板的内部来编写自己的覆盖,并且C ++程序员已经熟悉了这个概念。

例如,std::vector的序列化程序/反序列化程序可能类似于:

template<class T, class Alloc>
std::ostream& operator <<(std::ostream& os, const std::vector<T, Alloc>& vec)
{   os << vec.size << '\n';
    for(std::vector<T, Alloc>::const_iterator i = vec.begin();
        i != vec.end(); ++i)
        os << *vec << '\n';
    return os;
}

template<class T, class Alloc>
std::istream& operator >>(std::istream& is, std::vector<T, Alloc>& vec)
{   vec.clear();
    size_t size = 0;
    is >> size;
    vec.reserve(size);
    while(size--)
    {   T temp;
        is >> temp;
        vec.push_back(temp);
    }
    return is;
}

请注意,此方法有几个限制(留给读者练习)。评估这些并确定这是否是正确的方法是你的工作。

答案 3 :(得分:0)

我不知道任何标准程序,它取决于您存储的数据。你描述的是浅层和深层表示之间的区别(很像浅层或深层复制)。我建议根据存储的内容为每个类规划一个简化的序列化机制。正如您所指出的,当您具有与类实例不连续(或甚至不是其中一部分)的基础元素时,仅写出内存的字节表示是不够的。