定义
- 程序员在编写应用程序的时候往往需要将程序的某些数据存储在内存中,然后将其写入某个文件或是将它传输到网络中的另一台计算机上以实现通讯。这些过程将会涉及到程序数据转化成能被存储并传输的格式,因此被称为“序列化”(Serialization),而它的逆过程则可被称为“反序列化” (Deserialization)
- 简单来说,序列化就是将对象实例的状态转换为可保持或传输的格式的过程。与序列化相对的是反序列化,它根据流重构对象。这两个过程结合起来,可以轻松地存储和传输数据。例如,可以序列化一个对象,然后使用 HTTP 通过 Internet 在客户端和服务器之间传输该对象。
总结
- 序列化:将对象变成字节流的形式传出去。
- 反序列化:从字节流恢复成原来的对象。
序列化的好处
- 将对象存储于硬盘上 ,便于以后反序列化使用
- 在网络上传送对象的字节序列时便捷性、灵活性
C++对象序列化的四种方法
JSON文本序列化方式
- 使用jsoncpp或cJSON等库,将内存中的数据结构序列化为JSON格式的文本串。
Google Protocol Buffers(protobuf)(推荐)
目的
- Google Protocol Buffers (GPB)是Google内部使用的数据编码方式,旨在用来代替XML进行数据交换。可用于数据序列化与反序列化。主要特性有:高效;语言中立;可扩展
- 官方文档
- github地址
- 参考示例
Boost.Serialization(推荐)
目的
- Boost.Serialization可以创建或重建程序中的等效结构,并保存为二进制数据、文本数据、XML或者有用户自定义的其他文件。该库具有以下吸引人的特性: 代码可移植(实现仅依赖于ANSI C++);深度指针保存与恢复;可以序列化STL容器和其他常用模版库;数据可移植;非入侵性。
MFC Serialization (不推荐)
目的
- Windows平台下可使用MFC中的序列化方法。MFC 对 CObject 类中的序列化提供内置支持。因此,所有从 CObject 派生的类都可利用 CObject 的序列化协议
.Net Framework (不推荐)
目的
- .NET的运行时环境用来支持用户定义类型的流化的机制。它在此过程中,先将对象的公共字段和私有字段以及类的名称(包括类所在的程序集)转换为字节流,然后再把字节流写入数据流。在随后对对象进行反序列化时,将创建出与原对象完全相同的副本。
简单总结
- 其中MFC和.Net框架的方法适用范围很窄,只适用于Windows下,且.Net框架方法还需要.Net的运行环境
- 参考分析
- Google Protocol Buffers效率较高,但是数据对象必须预先定义,并使用protoc编译,适合要求效率,允许自定义类型的内部场合使用。
- Boost.Serialization 使用灵活简单,而且支持标准C++容器。
- 相比而言,MFC的效率较低,但是结合MSVS平台使用最为方便。
- 为了考虑平台的移植性、适用性和高效性,推荐大家使用Google的protobuf和Boost的序列化方案,下面介绍我使用这两种方案的心得及注意事项。
进一步学习
Google Protocol Buffers
- Google Protocol Buffers protobuf相对而言效率应该是最高的,不管是安装效率还是使用效率,protobuf都很高效,而且protobuf不仅用于C++序列化,还可用于Java和Python的序列化,使用范围很广。
protobuf支持的数据类型不是很丰富
- protobuf属于轻量级的,因此不能支持太多的数据类型,下面是protobuf支持的基本类型列表,一般都能满足需求,不过在选择方案之前,还是先看看是否都能支持,以免前功尽弃。同样该表也值得收藏,作为在定义类型时的参考依据。
.proto type | c++ | notes |
double | double |
|
float | float |
|
int32 | int32 | 使用可变长编码方式,负数时不够高效,应该使用sint32 |
int64 | int64 | 同上 |
uint32 | uint32 | 使用可变长编码方式 |
uint64 | uint64 | 同上 |
sint32 | int32 | 使用可变长编码方式,有符号的整型值,编码时比通常的int32高效 |
sint64 | sint64 | 同上 |
fixed32 | uint32 | 总是4个字节,如果数值总是比2^28大的话,这个类型会比uint32高效 |
fixed64 | uint64 | 总是8个字节,如果数值总是比2^56大的话,这个类型会比uint64高效 |
sfixed32 | int32 | 总是4个字节 |
sfixed64 | int64 | 总是8个字节 |
bool | bool |
|
string | string | 一个字符串必须是utf-8编码或者7-bit的ascii编码的文本 |
bytes | string | 可能包含任意顺序的字节数据 |
protobuf不支持二维数组(指针),不支持STL容器序列化
- 这个缺陷挺大,因为稍复杂点的数据结构或类结构里出现二维数组、二维指针和STL容器(set、list、map等)很频繁,但因为 protobuf简单的实现机制,只支持一维数组和指针(用repeated修饰符修饰),不能使用repeated repeated来支持二维数组, 也不支持STL,因此在选择该方案之前,一定 要确保你的数据结构里没有这些不支持的类型。
protobuf嵌套后会改变类名称
- protobuf支持类的嵌套,即在一个自定义类型中可以定义另一个自定义类型,但注意嵌套的自定义类型在经过protobuf处理后生成的类名称并不是你定义的类名称,而是加上了外层的类名称作为前缀
Boost.Serialization
- Boost库是个很庞大的库,功能非常丰富,序列化只是其中的一个小分支,但为了使用Boost的序列化方案,你需要安装整个Boost库,所花费的磁盘空间和时间都很多,同样支持的序列化功能也很强大,既支持二维数组(指针),也支持STL容器,更不需要我们用某种特殊的格式重新定义我们的类结构,其非侵入的性质使得我们无须改动已有的类结构即可序列化,这时非常赞的一个性质。
- 但是由于体积庞大,安装复杂,如果只是简单的序列化,没必要使用该方案,只有protobuf不能满足你的需求时,才应该考虑该方案。
- 安装boost库遇到的一系列问题 安装boost库本事就是一项很费时的工程,如果期间出现了各种错误,更加耗时耗耐心。
- 对于基本类型指针很难序列化
- 不能序列化变长数组
参考代码
- C++ 序列化探索
- 1)对基本数据类型char,short,int,long,string的序列化;
- 2)支持序列化为socket流;
- 3)支持对std::vector、std::list、std::set、std::map的序列化;
参考链接
- 最常用的两种C++序列化方案的使用心得(protobuf和boost serialization)