MFC 序列化机制

目录

文件操作相关类

序列化机制相关类

序列化机制使用

序列化机制执行过程

序列化类对象


文件操作相关类

CFile:文件操作类,封装了关于文件读写等操作,常见的方法:

  • CFile::Open:打开或者创建文件
  • CFile::Write/Read:写/读文件
  • CFile::Close:关闭文件
  • CFile::SeekToBegin/SeekToEnd/Seek:从 开始/结束/任意 位置设置文件读写位置

代码如下:

#include <afxwin.h>
#include <iostream>
using namespace std;
void File( ){CFile file;// 没有文件就创建,然后可读可写file.Open( "E:/MFC/Day07/file.txt", CFile::modeCreate|CFile::modeReadWrite );char str[] = "hello file";file.Write( str, strlen(str) );file.SeekToBegin( );// 设置文件读写位置char buf[256] = { 0 };long nLen = file.Read( buf, 255 ); // 返回值是实际读到的数据cout << buf << ' ' << nLen << endl;file.Close();
}
int main(){File();return 0;
}

序列化机制相关类

序列化作用:以二进制流形式读写硬盘文件,效率很高。

  • CFile:文件操作类,完成硬盘文件的读写操作
  • CArchive:归档类,完成内存数据的读写操作,维护了一个缓冲区

先把数据放到缓冲区,再放到硬盘上

序列化机制使用

序列化:往硬盘上写数据;

  1. 创建或打开文件  CFile::Open
  2. 定义归档类对象  CArchive ar;
  3. 数据序列化(存储/写)  ar<<数据   把数据读入缓冲区
  4. 关闭归档类对象,释放缓冲区
  5. 关闭文件

反序列化:从硬盘上读取数据。

  1. 打开文件  CFile::Open
  2. 定义归档类  CArchive  ar;
  3. 数据反序列化(加载/读)  ar>>变量
  4. 关闭文档类对象   ar.close()
  5. 关闭文件    CFile::Close()

代码如下:

#include <afxwin.h>
#include <iostream>
using namespace std;
void Store( ){//序列化(存储、写)数据CFile file;file.Open( "E:/MFC/Day07/serial.txt", CFile::modeCreate | CFile::modeWrite );CArchive ar(&file, CArchive::store, 4096);//归档类对象,维护缓冲区。long age = 18;ar << age;//将18保存当前指向的位置,并向后移动当前指向,相应字节数。float score = 88.5;ar << score;//将88.5保存当前指向的位置,并向后移动当前指向,相应字节数。CString name = "zhangsan";  ar << name;ar.Close( );file.Close( );
}
void Load( ){//反序列化(加载/读)CFile file;file.Open( "E:/MFC/day07/serial.txt", CFile::modeRead );CArchive ar( &file, CArchive::load, 4096 );//维护一个buff,大小4096字节long age;ar >> age;//当反序列化第一个数据时候,内部将文件中所有数据读入ar维护的buff中float score;ar >> score;//当反序列化后续数据时候,不需要到硬盘文件中读取,直接到ar维护的buff中读取CString name;ar >> name;//当反序列化后续数据时候,不需要到硬盘文件中读取,直接到ar维护的buff中读取ar.Close( );file.Close( );cout << age << ' ' << score << ' ' << name << endl;
}
int main(){Store( );Load( );return 0;
}

问题:数据一共16字节,为啥是17个字节

原因:首先排除\0,如果在内存中会有这个符号,但是再硬盘不会有。多出的一个字节是描述字符串大小的。前四个字节是Int,后四个字节是float,中间一个字节是字符串长度为8

Windows记事本解析文件是按照字符解析,所以前面的数字乱码了。

序列化机制执行过程

数据结构:

class CArchive
{	enum Mode;            // {store = 0,load = 1……}BOOL m_nMode;         // 访问方式int m_nBufSize;       // buff的大小CFile* m_pFile;       // 操作的文件对象BYTE* m_lpBufCur;     // 当前指向BYTE* m_lpBufMax;     // 终止指向BYTE* m_lpBufStart;   // 开始指向
}

CArchive ar(&file, CArchive::store, 4096);  构造函数伪代码如下:

CFile file;
file.Open( "E:/MFC/Day07/serial.txt", CFile::modeCreate | CFile::modeWrite );
CArchive ar(&file, CArchive::store, 4096) === CArchive::CArchive(&file,0, 4096)
{m_nMode = CArchive::store; // 0m_pFile = &file;//“E:/....serial.txt”m_nBufSize = 4096;m_lpBufStart = new BYTE[m_nBufSize];  // 开辟一块堆内存,指向首地址m_lpBufMax = m_lpBufStart + 4096;m_lpBufCur =  m_lpBufStart;
}

初始时

如何把数据存入缓冲区,伪代码如下:

long age = 18;
ar << age === CArchive::operator<<(age)//函数内部this为&ar
{if (m_lpBufCur + sizeof(LONG) > m_lpBufMax) {Flush();}*m_lpBufCur = age;m_lpBufCur += sizeof(LONG); 
} // 把18存入缓冲区,并且指针后移4个字节float score = 88.5;
ar << score === CArchive::operator<<(score)//函数内部this为&ar
{if (m_lpBufCur + sizeof(float) > m_lpBufMax) {Flush();}*m_lpBufCur = score;//88.5 m_lpBufCur += sizeof(float);
}CString name = "zhangsan";  
ar << name === CArchive::operator<<(name)//函数内部this为&ar
{AfxWriteStringLength(ar, 8 ){ar<<(unsigned char)nLength;//8}Write(name, 8)//函数内部this为&ar{memcpy_s(m_lpBufCur, (size_t)(m_lpBufMax - m_lpBufCur), name, 8);m_lpBufCur += 8;}
}

序列化三个数据后的缓冲区:

关闭文档类对象,释放缓冲区伪代码:把当前数据导入硬盘上,再重置当前指向

ar.Close( )//函数内部this为&ar
{Flush()//函数内部this为&ar{&file->Write(m_lpBufStart, ULONG(m_lpBufCur - m_lpBufStart));  // 往硬盘写数据m_lpBufCur = m_lpBufStart;//重置当前指向}
}

如何需要写入4个字节,但是只剩3个字节的空间,怎么办?

会调用flush(),把缓冲区的数据写入到硬盘空间,再重置当前指针指向。再重新写入数据,相当于从头覆盖写入。

序列化执行过程总结:

  1. ar对象维护一个缓冲区
  2. 将各个数据依次序列化(存储)到ar对象维护的缓冲区中,并将m_lpBufCur的指针指向移动相应字节
  3. 如果ar维护的缓冲区不足,则将ar维护的缓冲区的数据写入硬盘文件,并重置m_lpBufCur为开始指向
  4. 当关闭ar对象时,将ar对象维护的缓冲区数据写入硬盘文件,并释放ar对象维护的缓冲区。

反序列化执行过程总结:

  1. ar对象维护一个缓冲区
  2. 当反序列化第一个数据时,将文件数据全部读取到ar维护的缓冲区,并将第一个数据反序列化到第一个变量,并将m_lpBufCur移动相应的字节数
  3. 依次反序列化每个数据到变量中
  4. 当关闭ar对象时,释放ar维护的缓冲区

序列化类对象

序列化类对象的使用:

  • 类必须派生自CObject
  • 类内必须添加声明宏  DECLARE_SERIAL(theClass)
  • 类外必须添加实现宏   IMPLEMENT_SERIAL(theClass,baseClass,1)
  • 类必须重写虚函数 Serialize

当类具备以上四个条件时,类对象就可以序列化到文件保存了。

序列化类对象,除了要把类对象成员变量还有类对象的信息,类对象大小和版本等。

完整测试代码如下:

#include <afxwin.h>
#include <iostream>
using namespace std;class CMyDoc : public CDocument{DECLARE_SERIAL( CMyDoc )
public:CMyDoc(int age=0, float score=0.0, CString name=""):m_age(age),m_score(score),m_name(name){}int m_age;float m_score;CString m_name;virtual void Serialize( CArchive& ar );
};
IMPLEMENT_SERIAL( CMyDoc, CDocument, 1 )
void CMyDoc::Serialize( CArchive& ar ){if( ar.IsStoring() ){ar << this->m_age << this->m_score << this->m_name; //序列化基本类型变量  }else{ar >> m_age >> m_score >> m_name;//反序列化基本类型变量}
}
void Store( ){//序列化(存储、写)数据CFile file;file.Open("E:/MFC/Day08/serial.txt", CFile::modeCreate|CFile::modeWrite);CArchive ar(&file, CArchive::store, 4096);//归档类对象,维护缓冲区。CMyDoc data(18, 88.5, "zhangsan");ar << &data; //序列化对象,就是将对象的各个成员变量序列化。ar.Close( );file.Close( ); 
}
void Load( ){//反序列化(加载/读)CFile file;file.Open( "E:/MFC/day08/serial.txt", CFile::modeRead );CArchive ar( &file, CArchive::load, 4096 );//维护一个buff,大小4096字节CMyDoc* pdata = NULL;ar >> pdata;ar.Close( );file.Close( );cout << pdata->m_age << ' ' << pdata->m_score << ' ' << pdata->m_name << endl;
}
int main(){Store( );Load( );return 0;
}

测试结果:CMyDoc后面是成员变量的数据,前面是类对象信息

把 DECLARE_SERIAL( CMyDoc ) 宏展开

再进一步展开宏  _DECLARE_DYNCREATE

更进一步进入 _DECLARE_DYNAMIC

所以可以认为就是动态创建机制宏

实现宏展开

相当于动态创建机制加上一个操作符重载

下断点,分析一下类对象序列化执行

伪代码如下:

CFile file;
file.Open("E:/MFC/Day08/serial.txt", CFile::modeCreate|CFile::modeWrite);
CArchive ar(&file, CArchive::store, 4096);//归档类对象,维护缓冲区。
CMyDoc data(18, 88.5, "zhangsan");
ar << &data === operator<<(ar, const &data)
{ar.WriteObject(&data)//函数内部this为&ar{CRuntimeClass* pClassRef = &data->GetRuntimeClass();//文档类静态变量WriteClass(pClassRef);//将类的相关信息(类名/类大小/类版本)存入ar维护的buff中(&data)->Serialize(ar)//函数内部this为&data{ar << this->m_age << this->m_score << this->m_name; //序列化基本类型变量}}
}CFile file;
file.Open( "E:/MFC/day08/serial.txt", CFile::modeRead );
CArchive ar( &file, CArchive::load, 4096 );//维护一个buff,大小4096字节
CMyDoc* pdata = NULL;//????????????
ar >> pdata === operator>>(ar, pdata) 
{pdata = ar.ReadObject(RUNTIME_CLASS(CMyDoc))//函数内部this为&ar{CRuntimeClass* pClassRef = ReadClass(RUNTIME_CLASS(CMyDoc),...);//从文件读取 类的相关信息,和 RUNTIME_CLASS(CMyDoc)中信息进行比对,//如果相同返回RUNTIME_CLASS(CMyDoc),如果不同返回NULLCObject*pOb = RUNTIME_CLASS(CMyDoc)->CreateObject();//动态创建CMyDoc类的对象,并返回对象地址pOb->Serialize(ar)//函数内部this为刚刚创建的CMyDoc类对象(pOb){ar >> m_age >> m_score >> m_name;//反序列化基本类型变量}return pOb;}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/242303.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

经典目标检测YOLO系列(二)YOLOV2的复现(1)总体网络架构及前向推理过程

经典目标检测YOLO系列(二)YOLOV2的复现(1)总体网络架构及前向推理过程 和之前实现的YOLOv1一样&#xff0c;根据《YOLO目标检测》(ISBN:9787115627094)一书&#xff0c;在不脱离YOLOv2的大部分核心理念的前提下&#xff0c;重构一款较新的YOLOv2检测器&#xff0c;来对YOLOV2有…

k8s资源介绍

Kubernetes架构图 Kubernetes系统用于管理分布式节点集群中的微服务或容器化应用程序&#xff0c;并且其提供了零停机时间部署、自动回滚、缩放和容器的自愈&#xff08;其中包括自动配置、自动重启、自动复制的高弹性基础设施&#xff0c;以及容器的自动缩放等&#xff09;等…

SDL2 连续帧图像显示

QT使用SDL多窗口显示视频&#xff08;linux&#xff0c;ubuntu&#xff09;_linux qt sdl-CSDN博客 QT使用SDL播放YUV视频 - C - QT SDL调用OPENGL渲染图像 - C - 心得 C 使用SDL显示RGB图像数据_c sdl-CSDN博客 SDL库入门&#xff1a;掌握跨平台游戏开发和多媒体编程_sdl开…

【Python学习】Python学习21- 正则表达式(1)

目录 【Python学习】Python学习21- 正则表达式&#xff08;1&#xff09; 前言re.match函数实例 re.search方法re.match与re.search的区别参考 文章所属专区 Python学习 前言 本章节主要说明Python的正则表达式。 正则表达式是一个特殊的字符序列&#xff0c;它能帮助你方便的…

文心一言使用分享

ChatGPT 和文心一言哪个更好用&#xff1f; 一个直接可以用&#xff0c;一个还需要借助一些工具&#xff0c;还有可能账号会消失…… 没有可比性。 通用大模型用于特定功能的时候需要一些引导技巧。 import math import time def calculate_coordinate(c, d, e, f, g, h,…

Debian 10.13.0 安装图解

引导和开始安装 这里直接回车确认即可&#xff0c;选择图形化安装方式。 选择语言 这里要区分一下&#xff0c;当前选中的语言作为安装过程中安装器所使用的语言&#xff0c;这里我们选择中文简体。不过细心的同学可能发现&#xff0c;当你选择安装器语言之后&#xff0c;后续安…

WebDriverWait太强大

selenium webdriver及wait 1 implicitly包打天下2 Linkedin无法登录返回值很乱&#xff0c;怎么破&#xff1f; 1 implicitly包打天下 有了implicitly之后&#xff0c;基本上不再关注网速之类的影响。 self.driver.implicitly_wait(511)2 Linkedin无法登录返回值很乱&#xf…

【C语言编程之旅 6】刷题篇-for循环

第1题 解析 思路&#xff1a; 两个循环进行控制 外层循环控制打印多少行 内部循环控制每行打印多少个表达式以及表达式内容&#xff0c; 比较简单&#xff0c;具体参考代码 #include <stdio.h> int main() {int i 0;//控制行数for(i1; i<9; i){//打印每一行内容&am…

《PCI Express体系结构导读》随记 —— 第I篇 第2章 PCI总线的桥与配置(20)

接前一篇文章&#xff1a;《PCI Express体系结构导读》随记 —— 第I篇 第2章 PCI总线的桥与配置&#xff08;19&#xff09; 2.4 PCI总线的配置 PCI总线定义了两类配置请求&#xff0c;一个是Type 00h配置请求&#xff0c;另一个是Type 01h配置请求。PCI总线使用这些配置请求…

k8s 使用cert-manager证书管理自签

个人建议使用安装更快&#xff0c;比helm快&#xff0c;还要等待安装crd kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.13.3/cert-manager.yaml#官网 https://cert-manager.io/docs/installation/kubectl/#创建自签的ClusterIssuer c…

Linux设备管理模型-02:sysfs

文章目录 sysfs1 使用sysfs控制GPIO2 sysfs编程2.1 完善sysfs属性文件的读写操作 上一篇文: 设备管理模型中的基础数据结构 sysfs sysfs是用于导出内核对象的文件系统&#xff0c;它是一个基于ram的文件系统&#xff0c;最初基于ramfs。 sysfs通常挂载在/sys目录下。它提供了一…

【 Qt 快速上手】-②- Qt 环境搭建

文章目录 1. Qt 开发工具概述1.1 Qt Creator 介绍1.2 Visual Studio 介绍1.3 Eclipse 介绍 2. Qt SDK 的下载与安装2.1 Qt SDK 的下载2.2 Qt SDK 的安装2.3 验证 Qt SDK 安装是否成功2.4 Qt 环境变量配置 1. Qt 开发工具概述 Qt 开发环境需要安装三个部分&#xff1a; C编译器…

分布式系统——树状算法

1 树状算法的基本理论 树算法常用于在分布式系统中实现广播操作&#xff0c;这里您提供了一些基本定义和一个引理关于广播操作的消息复杂度和时间复杂度的下界。 1.1 广播定义 广播操作是由单个处理器&#xff08;源节点&#xff09;发起的&#xff0c;其目的是向系统中的所有…

systemverilog/verilog文件操作

1、Verilog文件操作 Verilog具有系统任务和功能,可以打开文件、将值输出到文件、从文件中读取值并加载到其他变量和关闭文件。 1.1 、Verilog文件操作 1.1.1、打开和关闭文件 module tb; // 声明一个变量存储 file handler integer fd; initial begin // 以写权限打开一个文…

互联网加竞赛 基于机器视觉的手势检测和识别算法

0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 基于深度学习的手势检测与识别算法 该项目较为新颖&#xff0c;适合作为竞赛课题方向&#xff0c;学长非常推荐&#xff01; &#x1f9ff; 更多资料, 项目分享&#xff1a; https://gitee.com/dancheng…

【react】创建react项目+项目结构

使用create-react-app快速搭建开发环境 create-react-app是一个快速创建React开发环境的工具&#xff0c;底层由Webpack构建&#xff0c;封装了配置细节 npx create-react-app react_hm执行命令后开始创建 创建好执行cd react_hm npm start 当看到webpack compiled successfu…

VMware workstation安装FreeBSD14.0虚拟机并配置网络

VMware workstation安装FreeBSD14.0虚拟机并配置网络 FreeBSD是类UNIX操作系统&#xff0c;FreeBSD带有多个软件包&#xff0c;并覆盖了广阔的应用领域&#xff0c;且都是免费和易于安装的。该文档适用于在VMware workstation平台安装FreeBSD14.0虚拟机。 1.安装准备 1.1安装…

【c++笔记】用c++解决一系列质数问题!

质数是c语言和c中比较常见的数学问题&#xff0c;本篇文章将带你走进有关质数的一系列基础问题&#xff0c;其中包含常见的思路总结&#xff0c;本篇文章过后&#xff0c;将会持续更新c算法系列&#xff0c;感兴趣的话麻烦点个关注吧&#xff01; 希望能给您带来帮助&#xff…

openjdk源码了解

openjdk给出debug配置选项&#xff0c;common/autoconf/jdk-options.m4 AC_DEFUN_ONCE([JDKOPT_SETUP_DEBUG_LEVEL], [################################################################################# Set the debug level# release: no debug information, all opti…

Python项目——计算器(PySide6+Pyinstaller)

1、介绍 使用python编写一个计算器&#xff0c;可以实现基本的运算。【注】该项目最终还有一些细小的bug没有完善&#xff0c;例如符号可以一直输入。 2、实现 使用pyCharm创建一个新的项目。 2.1、设计UI 使用Qt designer设计一个UI界面&#xff0c;保存ui文件&#xff0…