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; // 0
  m_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),如果不同返回NULL
    CObject*pOb = RUNTIME_CLASS(CMyDoc)->CreateObject();
           //动态创建CMyDoc类的对象,并返回对象地址
    pOb->Serialize(ar)//函数内部this为刚刚创建的CMyDoc类对象(pOb)
    {
      ar >> m_age >> m_score >> m_name;//反序列化基本类型变量
    }
    return pOb;
  }
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

mybatisPlus注解将List集合插入到数据库

1.maven引入依赖&#xff08;特别注意版本&#xff0c;3.1以下不支持&#xff09; <dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.4.3.1</version></dependency&g…

Android Studio读写低频RFID T5557卡源码

本示例使用的发卡器&#xff1a;https://item.taobao.com/item.htm?id675212889085&spma1z10.5-c.w4002-21818769070.13.21166f89nKgnJ7 <?xml version"1.0" encoding"utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xml…

SD-WAN组网设计原则:灵活、安全、高效

在实现按需、灵活和安全的SD-WAN组网方案中&#xff0c;我们必须遵循一系列关键的设计原则&#xff0c;以确保网络的可靠性和效率。通过以下几点设计原则&#xff0c;SD-WAN能够满足企业对灵活性、安全性和高效性的迫切需求。 灵活的Overlay网络互联 SD-WAN通过IP地址在站点之间…

linux基础学习(2):磁盘管理、分区、格式化

1.一些基本概念 一块磁盘从加入到可使用&#xff0c;需要经过3个阶段&#xff1a;分区-格式化-挂载。 1.1分区方式 linux有2种分区方式&#xff1a; &#xff08;1&#xff09;mbr&#xff1a;最大支持2.1T硬盘&#xff0c;最多支持4个分区。这4个分区可以全部为主分区&…

(设置非自定义Bean)学习Spring的第六天

一 . 获取Bean的方法详解 , 如下图 : 二 . Spring配置非自定义bean----DruidDatasource 我们举个例子 : 配置Druid数据源交由Spring管理 首先导入在pom文件Druid坐标 然后考虑 : 被配置的Bean的实例化方式是什么 : 无参构造 被配置的Bena是否要注入必要属性 : 四个基本信息…

防火墙技术

防火墙&#xff08;英语&#xff1a;Firewall&#xff09;技术是通过有机结合各类用于安全管理与筛选的软件和硬件设备&#xff0c;帮助计算机网络于其内、外网之间构建一道相对隔绝的保护屏障&#xff0c;以保护用户资料与信息安全性的一种技术。 防火墙技术的功能主要在于及…

查找局域网树莓派raspberry的mac地址和ip

依赖python库&#xff1a; pip install socket pip install scapy运行代码&#xff1a; import socket from scapy.layers.l2 import ARP, Ether, srpdef get_hostname(ip_address):try:return socket.gethostbyaddr(ip_address)[0]except socket.herror:# 未能解析主机名ret…

ARP相关

ARP报文格式&#xff1a; 目的以太网地址&#xff0c;48bit&#xff0c;发送ARP请求时&#xff0c;目的以太网地址为广播MAC地址&#xff0c;即0xFF.FF.FF.FF.FF.FF。 源以太网地址&#xff0c;48bit。 帧类型&#xff0c;对于ARP请求或者应答&#xff0c;该字段的值都为0x08…

iOS原生应用屏幕适配完整流程

1. 已iPhone 11 布局为设计布局,其他机型已这个来适配 2.变量与控件对应关系 txtViewer: txtAccount txtpwd seg btnOk 3.适配方法实现: //iOS屏幕适配 -(vo

设计PCB阻抗

https://zhuanlan.zhihu.com/p/589924395 1. 原理图设计 电路板的设计始于设计工程师设计电路原理图。 1.1, 工程师必须在原理图中指定受控阻抗信号&#xff0c;并将特定网络分类为差分对&#xff08;1002、902或852&#xff09;或单端网络&#xff08;402、502、552、602或75…

期末python实验一,二作业-对象编程【仅供参考】

目录 实验一西游游戏-对象 1&#xff0c;选择角色后&#xff0c;角色不能改 2&#xff0c;选择角色后&#xff0c;角色可以改 实验二猜拳游戏-对象 实验一西游游戏-对象 老师给的题目材料&#xff1a; 第一步&#xff1a;系统登录 失败时允许重复输入三次&#xff01; 第二…

设计 Mint.com

1. 梳理 User Case 和 约束 Use cases 作用域内的Use Case User 连接到 financial accountService 从 Account 中提取 transactions 日常 Update整理 transaction 所有的手动目录由 User 覆盖没有自动化的重排机制 - 通过目录分析月消费 Service 推荐 budget 允许 user 去…

小封装高稳定性振荡器 Sg2520egn / sg2520vgn, sg2520ehn / sg2520vhn

描述 随着物联网和ADAS等5G应用的实施&#xff0c;数据流量不断增长&#xff0c;网络基础设施变得比以往任何时候都更加重要。IT供应商一直在快速建设数据中心&#xff0c;并且对安装在数据中心内部/内部的光模块有很大的需求。此应用需要具有“小”&#xff0c;“低抖动”和“…

Redis分布式锁存在的问题以及解决方式

☆* o(≧▽≦)o *☆嗨~我是小奥&#x1f379; &#x1f4c4;&#x1f4c4;&#x1f4c4;个人博客&#xff1a;小奥的博客 &#x1f4c4;&#x1f4c4;&#x1f4c4;CSDN&#xff1a;个人CSDN &#x1f4d9;&#x1f4d9;&#x1f4d9;Github&#xff1a;传送门 &#x1f4c5;&a…

【前沿技术杂谈:智能对话的未来】深入比较ChatGPT与文心一言

【前沿技术杂谈&#xff1a;智能对话的未来】深入比较ChatGPT与文心一言 引言主体智能回复语言准确性知识库丰富度 深入分析&#xff1a;ChatGPT与文心一言的技术对比技术架构和算法数据处理和隐私用户界面和体验 应用场景分析未来展望技术进步的趋势潜在的挑战对社会的影响 结…

2018年认证杯SPSSPRO杯数学建模C题(第二阶段)机械零件加工过程中的位置识别全过程文档及程序

2018年认证杯SPSSPRO杯数学建模 基于轮廓提取与图像配准的零件定位问题研究 C题 机械零件加工过程中的位置识别 原题再现&#xff1a; 在工业制造自动生产线中&#xff0c;在装夹、包装等工序中需要根据图像处理利用计算机自动智能识别零件位置&#xff0c;并由机械手将零件…

JDBC编程详细教程与示例源码

版权声明 本文原创作者&#xff1a;谷哥的小弟作者博客地址&#xff1a;http://blog.csdn.net/lfdfhl JDBC概述 为了在Java语言中提供对数据库访问的支持&#xff0c;Sun公司于1996年提供了一套访问数据库的标准Java类库JDBC。JDBC的全称是Java数据库连接(Java Database Conn…

怎么样的布局是符合可制造性的PCB布局?

满足可制造性、可装配性、可维修性要求&#xff0c;方便调试的时候于检测和返修&#xff0c;能够方便的拆卸器件&#xff1a; 1&#xff09;极性器件的方向不要超过2种&#xff0c;最好都进行统一方向等要求&#xff0c;如图1-1所示&#xff1b; 图1-1 极性器件方向统一摆放 2…

CVE重要通用漏洞复现java php

在进行漏洞复现之前我们需要在linux虚拟机上进行docker的安装 我不喜欢win上安因为不知道为什么总是和我的vmware冲突 然后我的kali内核版本太低 我需要重新安装一个新的linux 并且配置网络 我相信这会话费我不少时间 查看版本 uname -a 需要5.5或以上的版本 看错了浪…

免费开源线上信息技术电子云书屋

1 概述 知命耳顺之际&#xff0c;时逢甲辰龙年到来&#xff0c;汇集半生研发积累和教育培训沉淀&#xff0c;以分布微服软件框架为基础&#xff0c;特别推出“线上电子云书屋”&#xff0c;陆续呈现编著的十余部信息技术教材和一些典型的软件架构平台&#xff0c;供给免费开源…