是否可以在C ++中序列化和反序列化一个类?
我已经使用Java 3年了,序列化/反序列化在这种语言中相当简单。 C ++有类似的功能吗?是否有处理序列化的本机库?
一个例子会有所帮助。
答案 0 :(得分:90)
Boost::serialization
库非常优雅地处理这个问题。我在几个项目中使用过它。有一个示例程序,展示如何使用它,here。
唯一可行的方法是使用流。这基本上就是Boost::serialization
库的所有功能,它通过设置框架将对象写入类似文本的格式并从相同的格式读取它来扩展stream方法。
对于内置类型或正确定义operator<<
和operator>>
的您自己的类型,这非常简单;有关详细信息,请参阅the C++ FAQ。
答案 1 :(得分:46)
我发现这是一个旧帖子,但它是搜索c++ serialization
时出现的第一个帖子。
我鼓励任何有权访问C ++ 11的人查看cereal,这是一个C ++ 11头文件库,用于序列化,支持二进制,JSON和XML开箱即用。谷物被设计为易于扩展和使用,并且具有与Boost类似的语法。
答案 2 :(得分:15)
提升是一个很好的建议。但如果你想自己动手,那就不那么难了。
基本上,您只需要一种方法来构建对象图,然后将它们输出为某种结构化存储格式(JSON,XML,YAML等等)。构建图形就像利用标记递归体面对象算法然后输出所有标记对象一样简单。
我写了一篇文章描述了一个基本的(但仍然很强大的)序列化系统。您可能会觉得有趣:Using SQLite as an On-disk File Format, Part 2。
答案 3 :(得分:13)
我推荐Google protocol buffers。 我有机会在一个新项目上测试驱动库,它非常容易使用。 该库已针对性能进行了大量优化。
Protobuf与其他序列化解决方案的不同之处在于它不会对您的对象进行序列化,而是根据您的规范为序列化的对象生成代码。
答案 4 :(得分:13)
Boost::serialization是一个不错的选择,但我遇到了一个新项目:Cereal我发现它更优雅!我强烈建议调查它。
答案 5 :(得分:11)
就“内置”库而言,<<
和>>
已专门用于序列化。
您应该覆盖<<
以将对象输出到某个序列化上下文(通常是iostream
)和>>
以从该上下文中读取数据。每个对象负责输出其聚合的子对象。
只要对象图不包含循环,此方法就可以正常工作。
如果确实如此,则必须使用库来处理这些周期。
答案 6 :(得分:4)
您可以查看amef协议,amef中的C ++编码示例就像是,
//Create a new AMEF object
AMEFObject *object = new AMEFObject();
//Add a child string object
object->addPacket("This is the Automated Message Exchange Format Object property!!","adasd");
//Add a child integer object
object->addPacket(21213);
//Add a child boolean object
object->addPacket(true);
AMEFObject *object2 = new AMEFObject();
string j = "This is the property of a nested Automated Message Exchange Format Object";
object2->addPacket(j);
object2->addPacket(134123);
object2->addPacket(false);
//Add a child character object
object2->addPacket('d');
//Add a child AMEF Object
object->addPacket(object2);
//Encode the AMEF obejct
string str = new AMEFEncoder()->encode(object,false);
java中的解码就像是,
string arr = amef encoded byte array value;
AMEFDecoder decoder = new AMEFDecoder()
AMEFObject object1 = AMEFDecoder.decode(arr,true);
协议实现具有C ++和Java的编解码器,有趣的是它可以以名称值对的形式保留对象类表示, 我在上一个项目中需要一个类似的协议,当我偶然发现这个协议时,我实际上根据我的要求修改了基本库。希望这会对你有所帮助。
答案 7 :(得分:3)
我建议使用其他海报所描述的boost序列化。这是一个关于如何使用它的详细教程,很好地补充了增强教程:http://www.ocoudert.com/blog/2011/07/09/a-practical-guide-to-c-serialization/
答案 8 :(得分:3)
Sweet Persist是另一个。
可以以XML,JSON,Lua和二进制格式对流进行序列化。
答案 9 :(得分:2)
我建议查看通常用作序列化基础的抽象工厂
我在另一个关于C ++工厂的问题中回答过。如果您感兴趣的是灵活的工厂,请参阅there。我尝试用ET ++描述一种旧方法来使用对我来说很有用的宏。
ET++是一个将旧MacApp移植到C ++和X11的项目。为此,Eric Gamma等开始考虑设计模式。 ET ++包含在运行时进行序列化和内省的自动方法。
答案 10 :(得分:0)
答案 11 :(得分:0)
这里是一个我简单的序列化程序库。仅标头,c11和 有序列化基本类型的示例。这是一张通往教室的地图。
https://github.com/goblinhack/simple-c-plus-plus-serializer
InputBox
输出:
#include "c_plus_plus_serializer.h"
class Custom {
public:
int a;
std::string b;
std::vector c;
friend std::ostream& operator<<(std::ostream &out,
Bits my)
{
out << bits(my.t.a) << bits(my.t.b) << bits(my.t.c);
return (out);
}
friend std::istream& operator>>(std::istream &in,
Bits my)
{
in >> bits(my.t.a) >> bits(my.t.b) >> bits(my.t.c);
return (in);
}
friend std::ostream& operator<<(std::ostream &out,
class Custom &my)
{
out << "a:" << my.a << " b:" << my.b;
out << " c:[" << my.c.size() << " elems]:";
for (auto v : my.c) {
out << v << " ";
}
out << std::endl;
return (out);
}
};
static void save_map_key_string_value_custom (const std::string filename)
{
std::cout << "save to " << filename << std::endl;
std::ofstream out(filename, std::ios::binary );
std::map< std::string, class Custom > m;
auto c1 = Custom();
c1.a = 1;
c1.b = "hello";
std::initializer_list L1 = {"vec-elem1", "vec-elem2"};
std::vector l1(L1);
c1.c = l1;
auto c2 = Custom();
c2.a = 2;
c2.b = "there";
std::initializer_list L2 = {"vec-elem3", "vec-elem4"};
std::vector l2(L2);
c2.c = l2;
m.insert(std::make_pair(std::string("key1"), c1));
m.insert(std::make_pair(std::string("key2"), c2));
out << bits(m);
}
static void load_map_key_string_value_custom (const std::string filename)
{
std::cout << "read from " << filename << std::endl;
std::ifstream in(filename);
std::map< std::string, class Custom > m;
in >> bits(m);
std::cout << std::endl;
std::cout << "m = " << m.size() << " list-elems { " << std::endl;
for (auto i : m) {
std::cout << " [" << i.first << "] = " << i.second;
}
std::cout << "}" << std::endl;
}
void map_custom_class_example (void)
{
std::cout << "map key string, value class" << std::endl;
std::cout << "============================" << std::endl;
save_map_key_string_value_custom(std::string("map_of_custom_class.bin"));
load_map_key_string_value_custom(std::string("map_of_custom_class.bin"));
std::cout << std::endl;
}
答案 12 :(得分:0)
我正在使用以下模板来实现序列化:
template <class T, class Mode = void> struct Serializer
{
template <class OutputCharIterator>
static void serializeImpl(const T &object, OutputCharIterator &&it)
{
object.template serializeThis<Mode>(it);
}
template <class InputCharIterator>
static T deserializeImpl(InputCharIterator &&it, InputCharIterator &&end)
{
return T::template deserializeFrom<Mode>(it, end);
}
};
template <class Mode = void, class T, class OutputCharIterator>
void serialize(const T &object, OutputCharIterator &&it)
{
Serializer<T, Mode>::serializeImpl(object, it);
}
template <class T, class Mode = void, class InputCharIterator>
T deserialize(InputCharIterator &&it, InputCharIterator &&end)
{
return Serializer<T, Mode>::deserializeImpl(it, end);
}
template <class Mode = void, class T, class InputCharIterator>
void deserialize(T &result, InputCharIterator &&it, InputCharIterator &&end)
{
result = Serializer<T, Mode>::deserializeImpl(it, end);
}
这里T
是要序列化的类型Mode
是伪类型,用于区分不同类型的序列化,例如。可以将相同的整数序列化为little endian,big endian,varint等。
默认情况下,Serializer
将任务委托给要序列化的对象。对于内置类型,您应该对Serializer
进行模板专业化。
还提供了便利功能模板。
例如无符号整数的小端序化:
struct LittleEndianMode
{
};
template <class T>
struct Serializer<
T, std::enable_if_t<std::is_unsigned<T>::value, LittleEndianMode>>
{
template <class InputCharIterator>
static T deserializeImpl(InputCharIterator &&it, InputCharIterator &&end)
{
T res = 0;
for (size_t i = 0; i < sizeof(T); i++)
{
if (it == end) break;
res |= static_cast<T>(*it) << (CHAR_BIT * i);
it++;
}
return res;
}
template <class OutputCharIterator>
static void serializeImpl(T number, OutputCharIterator &&it)
{
for (size_t i = 0; i < sizeof(T); i++)
{
*it = (number >> (CHAR_BIT * i)) & 0xFF;
it++;
}
}
};
然后序列化:
std::vector<char> serialized;
uint32_t val = 42;
serialize<LittleEndianMode>(val, std::back_inserter(serialized));
要反序列化:
uint32_t val;
deserialize(val, serialized.begin(), serialized.end());
由于抽象的迭代器逻辑,它应与任何迭代器(例如流迭代器),指针等配合使用。
答案 13 :(得分:0)