常用的c++序列化方法

简介: 常用的c++序列化方法

1、 什么是序列化?

程序员在编写应用程序的时候往往需要将程序的某些数据存储在内存中,然后将其写入某个文件或是将它传输到网络中的另一台计算机上以实现通讯。这个将 程序数据转化成能被存储并传输的格式的过程被称为“序列化”(Serialization),而它的逆过程则可被称为“反序列化” (Deserialization)。

简单来说,序列化就是将对象实例的状态转换为可保持或传输的格式的过程。与序列化相对的是反序列化,它根据流重构对象。这两个过程结合起来,可以轻 松地存储和传输数据。例如,可以序列化一个对象,然后使用 HTTP 通过 Internet 在客户端和服务器之间传输该对象。

总结:

序列化:将对象变成字节流的形式传出去。

反序列化:从字节流恢复成原来的对象。

序列化简化了对象的保存和载入,为对象提供了持久性。但是,序列化本身仍具有一定的局限性。由于序列化一次从文件中载入所有对象,因此,它不适合于大文件编辑器和数据库。对于数据库和大文件编辑器,它们每次只是从文件中读入一部分。此时,就不应该采用文档的序列化机制来直接读取和保存文件了。另外,使用外部文件格式(预先定义的文件格式而不是本应用程序定义的文件格式)的程序一般也不使用文档的序列化。

2、 为什么要序列化?好处在哪里?

简单来说,对象序列化通常用于两个目的:

(1) 将对象存储于硬盘上 ,便于以后反序列化使用

(2)在网络上传送对象的字节序列

对象序列化的好处在哪里?网络传输方面的便捷性、灵活性就不说了,这里举个我们经常可能发生的需求:你 有一个数据结构,里面存储的数据是经过很多其它数据通过非常复杂的算法生成的,由于数据量很大,算法又复杂,因此生成该数据结构所用数据的时间可能要很久 (也许几个小时,甚至几天),生成该数据结构后又要用作其它的计算,那么你在调试阶段,每次运行个程序,就光生成数据结构就要花上这么长的时间,无疑代价 是非常大的。如果你确定生成数据结构的算法不会变或不常变,那么就可以通过序列化技术生成数据结构数据存储到磁盘上,下次重新运行程序时只需要从磁盘上读 取该对象数据即可,所花费时间也就读一个文件的时间,可想而知是多么的快,节省了我们的开发时间。

3、最常用的两种序列化方案使用心得

3.1、Google Protocol Buffers

protobuf相对而言效率应该是最高的,不管是安装效率还是使用效率,protobuf都很高效,而且protobuf不仅用于C++序列化,还可用于Java和Python的序列化,使用范围很广。但在使用过程中要注意两个问题:

(1)protobuf支持的数据类型不是很丰富

protobuf属于轻量级的,因此不能支持太多的数据类型,下面是protobuf支持的基本类型列表,一般都能满足需求,不过在选择方案之前,还是先看看是否都能支持,以免前功尽弃。同样该表也值得收藏,作为我们在定义类型时做参考。

(2)protobuf不支持二维数组(指针),不支持STL容器序列化

这个缺陷挺大,因为稍复杂点的数据结构或类结构里出现二维数组、二维指针和STL容器(set、list、map等)很频繁,但因为 protobuf简单的实现机制,只支持一维数组和指针(用repeated修饰符修饰),不能使用repeated repeated来支持二维数组, 也不支持STL,因此在选择该方案之前,一定 要确保你的数据结构里没有这些不支持的类型。

(3)protobuf嵌套后会改变类名称

protobuf支持类的嵌套,即在一个自定义类型中可以定义另一个自定义类型,但注意嵌套的自定义类型在经过protobuf处理后生成的类名称并不是你定义的类名称,而是加上了外层的类名称作为前缀,下面举一个简单的例子:

message DFA {  
        required int32 _size = 1;  
        message accept_pair {  
          required bool is_accept_state = 1;  
          required bool is_strict_end = 2;  
          optional string app_name = 3;  
        }  
        repeated accept_pair accept_states = 2;  
    }

那么嵌套中的accept_pair 生成后的类不是accept_pair 而是DFA_accept_pair 。如果不想改类名称,将accept_pair 拿到外面与DFA平行定义即可。

3.2 Boost.Serialization

Boost库是个很庞大的库,功能非常丰富,序列化只是其中的一个小分支,但为了使用Boost的序列化方案,你需要安装整个Boost库,所花费的磁盘空间和时间都很多,同样支持的序列化功能也很强大,既支持二维数组(指针),也支持STL容器,更不需要我们用某种特殊的格式重新定义我们的类结构,其非侵入的性质使得我们无须改动已有的类结构即可序列化,这时非常赞的一个性质。但是由于体积庞大,安装复杂,如果只是简单的序列化,没必要使用该方案,只有protobuf不能满足你的需求时,才应该考虑该方案。

text_oarchive:文本序列化

binary_oarchive:二进制系列化

例子:

#include <iostream>
#include <boost\archive\text_oarchive.hpp>
#include <boost\archive\text_iarchive.hpp>
//#include <boost\archive\binary_oarchive.hpp>
//#include <boost\archive\binary_iarchive.hpp>
#include <string>
#include <fstream>
void Save()
{
  /*打开Test.bin 此种方法是利用构造函数打开*/
  std::ofstream file("Test.bin");
  /*定义oa为二进制存储类型*/
  boost::archive::text_oarchive oa(file);
  //boost::archive::binary_oarchive oa(file);
  int nNum = 0;
  std::cout << "输入int型" << std::endl;
  std::cin >> nNum;
  float fFloat = 0.0f;
  std::cout << "输入float型" << std::endl;
  std::cin >> fFloat;
  oa << nNum << fFloat;
  file.close();
}
void Load()
{
  std::ifstream file("Test.bin");
  /*定义ia为二进制读取类型*/
  boost::archive::text_iarchive ia(file);
  //boost::archive::binary_iarchive ia(file);
  /*读取顺序应和存储顺序相同 类型也需要相同*/
  int n = 0;
  float f = 0.0f;
  ia >> n >> f;
  std::cout << n << " " << f << std::endl;
  file.close();
}
int main()
{
  int nTemp = 0;
  std::cout << "1.输入并存储  2.读取并显示" << std::endl;
  std::cin >> nTemp;
  switch (nTemp)
  {
  case 1:
    Save();
    break;
  case 2:
    Load();
    break;
  default:
    break;
  }
  while (1);
  return 0;
}

相关文章
|
2月前
|
存储 Java C++
C++ 引用和指针:内存地址、创建方法及应用解析
C++中的引用是现有变量的别名,创建时需用`&`运算符,如`string &meal = food;`。指针存储变量的内存地址,使用`*`创建,如`string* ptr = &food;`。引用必须初始化且不可为空,而指针可初始化为空。引用在函数参数传递和提高效率时有用,指针适用于动态内存分配和复杂数据结构操作。选择使用取决于具体需求。
58 9
|
1月前
|
算法 Linux C++
C++框架设计中实现可扩展性的方法
在软件开发中,可扩展性至关重要,尤其对于C++这样的静态类型语言。本文探讨了在C++框架设计中实现可扩展性的方法:1) 模块化设计降低耦合;2) 使用继承和接口实现功能扩展;3) 通过插件机制动态添加功能;4) 利用模板和泛型提升代码复用;5) 遵循设计原则和最佳实践;6) 应用配置和策略模式以改变运行时行为;7) 使用工厂和抽象工厂模式创建可扩展的对象;8) 实现依赖注入增强灵活性。这些策略有助于构建适应变化、易于维护的C++框架。
40 2
|
8天前
|
JSON NoSQL Java
Redis18的Java客户端-StringRedisTemplate,序列化存在的问题,使用StringRedisTemplate解决序列化的方法
Redis18的Java客户端-StringRedisTemplate,序列化存在的问题,使用StringRedisTemplate解决序列化的方法
|
1月前
|
C++ 存储 Java
C++ 引用和指针:内存地址、创建方法及应用解析
'markdown'C++ 中的引用是现有变量的别名,用 `&` 创建。例如:`string &meal = food;`。指针通过 `&` 获取变量内存地址,用 `*` 创建。指针变量存储地址,如 `string *ptr = &food;`。引用不可为空且不可变,指针可为空且可变,适用于动态内存和复杂数据结构。两者在函数参数传递和效率提升方面各有优势。 ```
|
19天前
|
存储 编译器 程序员
C++语言速成方法
C++语言速成方法
|
19天前
|
C++ UED 开发者
逆向学习 MFC 篇:视图分割和在 C++ 的 Windows 窗口程序中添加图标的方法
逆向学习 MFC 篇:视图分割和在 C++ 的 Windows 窗口程序中添加图标的方法
12 0
|
1月前
|
C++ 安全
高效遍历:C++中分隔字符串单词的3种方法详解与实例
拷贝并交换(Copy-and-Swap)是C++中实现赋值操作符和异常安全拷贝构造函数的技巧。它涉及创建临时对象,使用拷贝构造函数,然后交换数据以确保安全。C++11之前的策略在此后及C++11引入的移动语义和右值引用下仍有效,但后者提供了更高效的实现方式。
|
2月前
|
JSON Java Linux
【探索Linux】P.30(序列化和反序列化 | JSON序列化库 [ C++ ] )
【探索Linux】P.30(序列化和反序列化 | JSON序列化库 [ C++ ] )
38 2
|
2月前
|
C语言 C++
C++|运算符重载(2)|运算符重载的方法与规则
C++|运算符重载(2)|运算符重载的方法与规则
|
2月前
|
编译器 C++
C++ 解引用与函数基础:内存地址、调用方法及声明
C++ 中的解引用允许通过指针访问变量值。使用 `*` 运算符可解引用指针并修改原始变量。注意确保指针有效且不为空,以防止程序崩溃。函数是封装代码的单元,用于执行特定任务。理解函数的声明、定义、参数和返回值是关键。函数重载允许同一名称但不同参数列表的函数存在。关注公众号 `Let us Coding` 获取更多内容。
153 1

热门文章

最新文章