MFC 序列化机制

这篇具有很好参考价值的文章主要介绍了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个字节

MFC 序列化机制,MFC,mfc,c++

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

MFC 序列化机制,MFC,mfc,c++

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;
}

初始时

MFC 序列化机制,MFC,mfc,c++

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

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;
  }
}

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

MFC 序列化机制,MFC,mfc,c++

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

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后面是成员变量的数据,前面是类对象信息

MFC 序列化机制,MFC,mfc,c++

把 DECLARE_SERIAL( CMyDoc ) 宏展开

MFC 序列化机制,MFC,mfc,c++

再进一步展开宏  _DECLARE_DYNCREATE

MFC 序列化机制,MFC,mfc,c++

更进一步进入 _DECLARE_DYNAMIC

MFC 序列化机制,MFC,mfc,c++

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

MFC 序列化机制,MFC,mfc,c++

实现宏展开

MFC 序列化机制,MFC,mfc,c++

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

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

MFC 序列化机制,MFC,mfc,c++

伪代码如下:文章来源地址https://www.toymoban.com/news/detail-807576.html

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;
  }
}

到了这里,关于MFC 序列化机制的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 序列化,反序列化之实例

    介绍文章 __construct() 当一个对象创建时自动调用 __destruct() 当对象被销毁时自动调用 (php绝大多数情况下会自动调用销毁对象) __sleep() 使**用serialize()函数时触发 __wakeup 使用unserialse()**函数时会自动调用 __toString 当一个对象被当作一个字符串被调用 __call() 在对象上下文中调用不

    2024年02月14日
    浏览(45)
  • Qt 对象序列化/反序列化

    阅读本文大概需要 3 分钟 日常开发过程中,避免不了对象序列化和反序列化,如果你使用 Qt 进行开发,那么有一种方法实现起来非常简单和容易。 我们知道 Qt 的元对象系统非常强大,基于此属性我们可以实现对象的序列化和反序列化操作。 比如有一个学生类,包含以下几

    2024年02月13日
    浏览(42)
  • 【网络】协议定制+序列化/反序列化

    如果光看定义很难理解序列化的意义,那么我们可以从另一个角度来推导出什么是序列化, 那么究竟序列化的目的是什么? 其实序列化最终的目的是为了对象可以 跨平台存储,和进行网络传输 。而我们进行跨平台存储和网络传输的方式就是IO,而我们的IO支持的数据格式就是

    2024年02月08日
    浏览(43)
  • 协议,序列化,反序列化,Json

    协议究竟是什么呢?首先得知道主机之间的网络通信交互的是什么数据,像平时使用聊天APP聊天可以清楚,用户看到的不仅仅是聊天的文字,还能够看到用户的头像昵称等其他属性。也就可以证明网络通信不仅仅是交互字符串那么简单。事实上网络通信还可能会通过一个结构

    2024年02月13日
    浏览(40)
  • Spring Boot 序列化、反序列化

    在软件开发中,序列化和反序列化是一种将对象转换为字节流以便存储或传输的机制。序列化将对象转换为字节流,而反序列化则将字节流转换为对象。序列化和反序列化在许多应用场景中都起着重要的作用,比如在网络通信中传输对象、将对象存储到数据库中、实现分布式

    2024年02月15日
    浏览(43)
  • Unity-序列化和反序列化

    序列化是指把对象转换为字节序列的过程,而反序列化是指把字节序列恢复为对象的过程。序列化最主要的用途就是传递对象和保存对象。 在Unity中保存和加载、prefab、scene、Inspector窗口、实例化预制体等都使用了序列化与反序列化。 1 自定义的具有Serializable特性的非抽象、

    2024年01月24日
    浏览(57)
  • 【Linux】序列化和反序列化

    在网络编程中,直接使用 结构体 进行数据传输会出错,因为 本质上socket无法传输结构体 ,我们只有将结构体装换为字节数组,或者是字符串格式来传输,然后对端主机收到了数据,再将其转化为结构体,这就是序列化和反序列化的过程! 序列化 (Serialization)是将对象的状态

    2024年02月10日
    浏览(43)
  • Java序列化和反序列化

    目录 一、序列化和反序列化 二、Java序列化演示 三、反序列化漏洞 1、含义 ​序列化就是内存中的对象写入到IO流中,保存的格式可以是二进制或者文本内容。反序列化就是IO流还原成对象。 2、用途 (1)传输网络对象 (2)保存Session 1、序列化 java.io.ObjectOutputStream代表对象

    2023年04月25日
    浏览(39)
  • 什么是序列化和反序列化?

    JSON(JavaScript Object Notation)和XML(eXtensible Markup Language)是两种常用的数据交换格式,用于在不同系统之间传输和存储数据。 JSON是一种轻量级的数据交换格式,它使用易于理解的键值对的形式表示数据。JSON数据结构简单明了,易于读写和解析,是基于JavaScript的一种常用数据

    2024年02月09日
    浏览(58)
  • 协议定制 + Json序列化反序列化

    1.1 结构化数据 协议是一种 “约定”,socket api的接口, 在读写数据时,都是按 “字符串” 的方式来发送接收的。如果我们要传输一些\\\"结构化的数据\\\" 怎么办呢? 结构化数据: 比如我们在QQ聊天时,并不是单纯地只发送了消息本身,是把自己的头像、昵称、发送时间、消息本身

    2024年02月09日
    浏览(46)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包