在C++中保存复杂对象的方法
Methods of saving complex objects in C++
我总是通过将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::序列化库,它看起来也很不优雅,而且异常繁琐——也就是说,如果这是正确的方法,我只会编写自己的序列化方法。
否。使用Boost.Serialization或Google协议缓冲区。是的,你必须编写函数,将你的数据放入串行化容器并从中提取。这就是为预期实际工作的健壮解决方案所做的工作。
通过这种方式,您可以获得二进制文件的版本控制、兼容性和可移植性。如果您将数据视为一堆字节,并写入/读取所有内容,那么当您更改结构时,或者当文件是从具有不同填充/字节大小/字节顺序的构建中写入时,您将无法读取旧文件。
它可能适用于简单的事情,但会很快崩溃,你会后悔没有从一开始就做好。
您提到的的两种策略
A) 编写复杂的序列化算法,将每个复杂的数据类型转换为基元,然后保存为二进制;或
B) 只需按照我最初的策略将所有内容写入ASCII文件即可。
是通常的做法。您实际上是在创建自己的文件格式。最常见的范式是大块范式。保存一个对象或一组对象时,首先要写一个int,表示对象或数据"块"的大小。下一个int表示对象的种类。如果您关心在用户升级软件时保存的支持配置,则可能还需要包含版本信息。
当您关心数据是否非常精确时,选项A非常有用,它使在c++中加载/保存有问题变得更容易。例如,以这种方式保存的浮点值将加载与保存的值完全相同的值。
当你想查看你正在保存的内容时,选项B很有用,也许对于一个人来说,可以用某种方式手动修改数据。保存在此处的浮子在装载回时将不完全相同。
请尝试查看其他文件格式的示例。Midi文件格式使用chunks范式,并具有流式传输功能,使用选项a。Wavefront"obj"文件格式用于3D应用程序,因为它使用选项B非常简单。所有内容都可以在您喜欢的文本编辑器中读取。
如果你想坚持基于文本的序列化,你可以尝试覆盖:
- 用于序列化的
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;
}
请注意,这种方法有几个限制(留给读者练习)。你的工作是评估这些,并决定这是否是正确的方法。
我不知道有任何标准过程,这取决于您存储的数据。你所描述的是浅层和深层表达之间的区别(很像浅层或深层复制)。我建议根据存储的内容为每个类规划一个简化的序列化机制。正如您所指出的,当您的底层元素与类实例不连续(甚至是不连续的一部分)时,仅仅写出内存的字节表示是不够的。
- 使用std::函数映射对象方法
- 正在调用shared_ptr对象方法
- 检查哪个对象调用了另一个对象的对象方法
- 如何在C++中循环访问未知对象方法?
- c++ 替换调用对象方法的宏函数
- 我能否通过将函数实现为类对象方法来避免使用互斥锁
- 我可以制作一个对象方法,如果单独调用,它将自行修改,但如果在复制初始化期间调用,则会返回一个新对象?
- 如何让两个不同的对象方法相互用作参数
- 在创建 c++ 期间调用对象方法
- 扩展中的日期时间对象方法C++问题
- 将对象方法从向量应用于C 的第二个向量中的对象
- 将 C++/CLI 对象指针传递给本机对象方法
- 多态性的面向对象方法
- 将对象方法传递到变量
- C :使用Main中定义的对象方法的函数
- 有关使用矢量迭代器访问对象方法的问题
- 定义可以执行对象方法和独立函数的函数时出现问题
- 调用列表中子对象方法的最佳方法
- main.cpp:(.text+0x8f):对[对象方法]的未定义引用
- cpp 从需要超类对象的函数访问子类对象方法