需要使用到序列化场景的需求

  • 在写代码的过程中,经常会需要把代码层面的对象数据保存到文件,而这些数据会以各种格式存储.例如:json,xml,二进制等等.二进制,相比json,xml格式,直接把字节copy到硬盘,所以这实现起来相对容易.

例子

  • 下面是一个简单的三维向量结构体,如何把它序列化到文件呢?

struct Vec3 {float x;float y;float z;
}
Vec3 v;
v.x = 1.0f;
v.y = 2.0f;
v.z = 3.0f;
os.write((const char *)&v, sizeof(Vec3));
  • 上述是序列化Vec3对象数据到文件的代码,非常直接.它的内存布局是3个浮点型变量紧凑排列,要把它存储到硬盘,只要从头到尾按字节拷贝即可.但是,在实际开发中,要序列化的对象不可能全部都是内存紧凑排列的,例如STL容器.
std::vector<Vec3> vec;
  • 如果将容器变量从头到尾拷贝到文件,必然会出现错误.因为容器内部通过一个指针来访问存储的对象,而直接拷贝这个容器,只会把指针拷贝,指针指向的数据却丢失了.但是,容器提供了一个可以直接访问指针指向数据的接口,可以通过这个接口得到数据然后直接拷贝.
os.write((const char *)&vec, vec.size() * sizeof(Vec3));        //  错误, 仅拷贝指针
os.write((const char *)vec.data(), vec.size() * sizeof(Vec3));  //  正确, 数据被完全拷贝
  • 通过这个方法就可以得到正确的拷贝结果了.通常,好的做法是将序列化和反序列化封装成接口,以便于使用,如何封装接口,就是这篇文章的主题.从上述两个例子可以发现,对于单体对象和数组对象,编写的代码是不一样的,单体对象直接拷贝,数组对象需要通过 .data() 取得数据地址再进行拷贝.而考虑到还有嵌套数组像 std::vector<std::vector<Vec3>>.对于嵌套数组序列化的代码可能如下:
std::vector<std::vector<Vec3>> vec2;
for (auto & vec: vec2)
{os.write((const char *)vec.data(), vec.size() * sizeof(Vec3));
}
  • 可以发现,对嵌套数组对象的序列化代码跟上述2种对象又不一样,考虑到还有N层嵌套的数组对象.此外,在C++中有一个 可平凡复制的概念 ,通俗的说,就是可以直接按字节拷贝的结构称之为 可平凡复制 ,上述的 Vec3 则是一个可平凡复制结构,而STL容器则不是可平凡复制结构,除此之外还有更多不可平凡复制且非容器的结构,故此,如果要封装接口,除了区分单体对象和数组对象,还要区分可平凡复制和不可平凡复制.
void Serialize(std::ostream & os,   const Type & val);  //  序列化
void Deserialize(std::istream & is,       Type & val);  //  反序列化
//  POD
template <class T, typename std::enable_if_t<std::is_trivially_copyable_v<T>, int> N = 0>
void Serialize(std::ostream & os, const T & val)
{os.write((const char *)&val, sizeof(T));
}//  容器
template <class T, typename std::enable_if_t<std::is_same_v<typename T::iterator, decltype(std::declval<T>().begin())> &&std::is_same_v<typename T::iterator, decltype(std::declval<T>().end())>, int> N = 0>void Serialize(std::ostream & os, const T & val)
{unsigned int size = val.size();os.write((const char *)&size, sizeof(size));for (auto & v : val) { Serialize(os, v); }
}//  POD
template <class T, typename std::enable_if_t<std::is_trivially_copyable_v<T>, int> N = 0>
void Deserialize(std::istream & is, T & val)
{is.read((char *)&val, sizeof(T));
}//  容器
template <class T, typename std::enable_if_t<std::is_same_v<typename T::iterator, decltype(std::declval<T>().begin())> &&std::is_same_v<typename T::iterator, decltype(std::declval<T>().end())>, int> N = 0>void Deserialize(std::istream & is, T & val)
{unsigned int size = 0;is.read((char *)&size, sizeof(unsigned int));val.resize(size);for (auto & v : val) { Deserialize(is, v); }
}
  • 以上实现可序列化任意 可平凡拷贝 结构,并且也可序列化任意嵌套层数的STL风格数组.而对于 不可平凡复制 结构,只需要针对该结构重载即可.借助C++强大的类型推导机制和SFINEA机制,可保证类型安全又具备可扩展性.

C++学习 优雅的实现对象到文件的序列化/反序列化 关键字serialize相关推荐

  1. php+打开文件和其子文件,【php学习记录】 引用、打开文件

    [php学习记录] 引用.打开文件 一.PHP echo 和 print 语句 echo 和 print 区别: echo - 可以输出一个或多个字符串 print - 只允许输出一个字符串,返回值总 ...

  2. java学习笔记16--I/O流和文件

    本文地址:http://www.cnblogs.com/archimedes/p/java-study-note16.html,转载请注明源地址. IO(Input  Output)流 IO流用来处理 ...

  3. 学习强制删除正在运行的文件

    看雪软件安全论坛 > 软件安全 > 『安全编程论坛』 > [分享]学习强制删除正在运行的文件 PDA 查看完整版本 : [分享]学习强制删除正在运行的文件 yaolibing 200 ...

  4. vba fso读utf 文本_利用FSO对象操作文件

    大家好,我们今日讲解"VBA信息获取与处理"教程中第十八个专题"FSO对象对文件及文件夹的处理"的第二节"利用FSO对象操作文件",这个专题 ...

  5. AIR文件操作(三):使用FileStream对象读写文件

    快速上手例: 例1.读xml var testXML:XML; var file:File = File.documentsDirectory.resolvePath("Mousebomb/ ...

  6. python学习笔记4(对象/引用;多范式; 上下文管理器)

    python学习笔记4(对象/引用:多范式; 上下文管理器) ### Python的强大很大一部分原因在于,它提供有很多已经写好的,可以现成用的对象 21. 动态类型:对象/引用 对象和引用: 对象是 ...

  7. java 注解 对象_Java基础-学习使用Annotation注解对象

    Java基础-学习使用Annotation注解对象 注解(也被称为元数据)为我们在代码中添加信息提供了一种形式化的方法,使我们可以在稍后某 个时刻非常方便地使用这些数据 1-1:基本语法 Java S ...

  8. Servlet的学习之Request请求对象(3)

    本篇接上一篇,将Servlet中的HttpServletRequest对象获取RequestDispatcher对象后能进行的[转发]forward功能和[包含]include功能介绍完. 首先来看R ...

  9. DSP学习(8)—— linker.cmd文件解析

    DSP学习(8)-- linker.cmd文件解析 文章目录 DSP学习(8)-- linker.cmd文件解析 前言 前言 写工程的时候遇到报内存不够的错误,出现在linker.cmd的内存分配se ...

最新文章

  1. 【FFmpeg】Hello World!尝试如何编译FFmpeg程序
  2. zabbix 中 mysql.sock 丢失问题
  3. 计算机的硬盘和光盘数,硬盘和光盘属于什么媒体
  4. webpack文章(持续更新)
  5. OSI 网络协议模型为什么是 7 层?
  6. java面试宝典 多线程,《java面试宝典》之java多线程面试题
  7. 我的第一个wp8小程序
  8. java foreach break_Java8中的foreach跳出循环break/return
  9. SQL纯手写创建数据库到表内内容
  10. c语言中如何通过二级指针来操作二维数组
  11. 02 - Tomcat配置
  12. 删除root 家目录,及恢复方法
  13. CentOs 虚拟LINUX系统安装与虚拟环境配置
  14. Java Web程序设计
  15. 【学术相关】明尼苏达大学博导“约法十章”火了:没事不乱开会、合写论文不要催导师,复旦教授直呼值得学习...
  16. SpringBoot下载Excel文件,在Wps上可以打开但是Office上的excel打不开的问题
  17. 家谱树java_树家族算法梳理
  18. 爱奇艺播放技术——300ms背后的故事
  19. java emoji表情 乱码_java 微信昵称带有emoji 表情乱码
  20. 基于已知点云地图的NDT的激光SLAM定位

热门文章

  1. golang调用java的函数_大话golang性能分析(一):profile基本原理
  2. linux getdents 例子,Linux内建命令和外部命令(整理)
  3. 基于C#的AE+IDL二次桌面端程序开发
  4. 神经网络与深度学习——TensorFlow2.0实战(笔记)(四)(python模块、包和库)
  5. 【转】聊聊Linux操作系统中的显示管理器及如何更换
  6. ASP.NET MVC 入门1、简介
  7. 第一节:从面向对象思想(oo)开发、接口、抽象类以及二者比较
  8. 【转】TFS签入签出规范
  9. 一步步编写操作系统 69 汇编语言和c语言共同协作 70
  10. 指针右左法则----复杂指针解析