《VC++深入详解》学习笔记 第十三章 文档与串行化

本文详细介绍了MFC中CArchive类在文件读写中的应用,包括如何利用CArchive进行对象串行化,以及MFC框架中的文档新建功能。文章通过CGraphicDoc类展示了如何实现Serialize函数,探讨了文档管理器、文档模板和视图之间的关系。此外,还讨论了可串行化类的实现步骤和CObArray在串行化中的作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.使用CArchive类对文件进行读写操作

1MFC提供的CArchive类没有基类,可以用它将对象数据保存到永久设备上,这种让对象数据持久性的过程就称为串行化(或称为序列化)

2)在程序中使用CArchive对象时,首先要在创建CArchive对象之前必须先创建一个CFile类或其派生类对象,并且因为存档对象既可以用来加载数据,也可以用来保存数据,所以要确保这个CFile类对象的打开方式与该存档对象的加载/保存状态相一致,当构造了一个CArchive对象后,就可以将它与一个代表某个打开文件的CFile类对象或其派生类对象相关联(一个文件只能与一个活动的存档对象相关联)

3CArchive对象不仅可以处理基本类型的数据,还可以处理CObject类的派生类对象,在CArchive类中重载了>><<操作符

4)构造函数:CArchiveCFile* pFile,UINT nMode,int nBufSize = 4096,void* lpBuf = NULL;

// pFile指向文件对象的指针,该文件对象是持久数据的最终来源或目的地

//nMode指定对象是被用来加载的,还是用来保存的标识

// nBufSize指定内部文件缓冲区的大小(字节)

//可选指针,指向用户提供的大小为nBuffSize的缓冲区,如果未指定这个参数,那么存档对象将从应用程序的局部堆中分配一块缓冲区,并且该对象销毁时将释放这块内存

5)示例:CFile file(“1.txt”,CFile::modeCreate|CFile::modeWrite);

           CArchive ar(&file,CArchive::store);

           int i = 4;

           char ch = ‘a’;

           CString str(“aaa”);

           Ar<<i<<ch<<str;

2.MFC框架程序提供的文件新建功能

1CGraphicDoc类中OnNewDocument函数,它是由框架调用的一个虚函数

2)程序启动时,会建立一个文档,文档的默认标题为“无标题”,我们可以在OnNewDocument这个函数中设置文档的标题:CDocument类中的成员函数:SetTitle

3IDR_MAINFRAME字符串资源也可以用来改变文档的标题,在CGraphicApp类的InitInstance函数中,将字符串资源传递给单文档模板对象。

     CSingleDocTemplate* pDocTemplate;

          pDocTemplate = new CSingleDocTemplate(

                   IDR_MAINFRAME,//这里IDR_MAINFRAME资源不仅表示字符串资源,还表示菜单、图标等资源

                   RUNTIME_CLASS(CGraphicDoc),

                   RUNTIME_CLASS(CMainFrame),       // main SDI frame window

                   RUNTIME_CLASS(CGraphicView));

         AddDocTemplate(pDocTemplate);

     在构造单文档模板对象时,将文档类、框架类、视类都作为参数传递给该对象,也就是通过单文档模板对象将这几个类关联在一起,接下类,程序调用AddDocTemplate函数将已构造的文档模板对象加入到应用程序的文档模板链中,这时就把IDR_MAINFRAME字符串传递给应用程序的框架了

     IDR_MAINFRAME字符串资源,我们可以通过文档模板类的成员函数GetDocString来获取各个字串

4OnNewDocument函数的调用过程

    OnNewDocument函数是文件新建功能的一部分(程序启动时,或单击【新建】菜单项都会调用此函数)在新建菜单项的响应函数中间接调用到此函数

    程序启动时OnNewDocument函数的调用流程:

程序启动——CWinApp::OnFileNewm_pDocManager->OnFileNew())——CDocManager::OnFileNewpTemplate->OpenDocumentFile(NULL))——CSingleDocTemplate::OpenDocumentFileCreateNewDocumentCreateNewFramepDocument->OnNewDocument)——CGraphicDoc::OnNewDocument

        单文档应用程序单击文件新建菜单后OnNewDocument函数的调用流程:

菜单命令——CWinApp::OnFileNewm_pDocManager->OnFileNew())——CDocManager::OnFileNewpTemplate->OpenDocumentFile(NULL))——CSingleDocTemplate::OpenDocumentFilepDocument->OnNewDocument)——CGraphicDoc::OnNewDocument

5)文档管理器利用指针链表来维护文档模板的指针,文档模板管理文档类、框架类和视类对象

MFC提供的文档/视图结构的特点:每有一份文档产生时,总是会产生一个文档类对象、框架类对象和视类对象,它们三位一体来为这份文档服务

3.文档串行化

1CGraphicDoc类的Serialize函数,该函数有一个参数,类型是CArchive引用类型

     在此函数中,首先判断ar的当前状态,如果是存储状态,则在其后增加相应的存储代码,否则在else子块中增加加载数据的代码

     在使用【保存】和【打开】菜单项时会进入这个函数,这个函数就是文档类提供的用来保存和加载数据的函数,我们可以利用其参数提供的CArchive对象来保存或加载我们自己的数据

2)文档/视图结构中,文档类负责管理数据,提供对数据的保存和加载,视类负责显示数据,为用户提供编辑数据和修改数据的功能

3MFC框架对Serialize函数的调用过程

文件打开命令——CWinApp::OnFileOpenm_pDocManager->OnFileOpen())—CDocManager::OnFileOpen(调用DoPromptFileName函数显示文件打开对话框—AfxGetApp()->OpenDocumentFile)——CWinApp::OpenDocumentFile(m_pDocManager->OpenDocumentFile)——CDocManager::OpenDocumentFile(pTemplate->MatchDocType(szPath,pOpenDocument)pBestTemplate->OpenDocumentFile)——CSingleDocTemplate::OpenDocumentFile(pDocument->

OnOpenDoument)——CDocument::OnOpenDocument(构造一个CFile::modeRead模式的CFile对象—构造一个CArchive::load模式的CArchive对象—SerializeloadArchive)——CGraphicDoc::Serialize

           当保存一个文件后再次打开这个文件不会调用Serialize函数,因为运行到CDocManager::OpenDocumentFile时,因为pOpenDocument这个指针有值了,所以这个函数直接返回pOpenDocument这个文档类指针,这个函数就结束了,不再往下执行。

4)对于多文档程序来说,每打开一个文件都回构造一个新的文档对象,对单文档来说,文档对象本身并不会销毁,它只是将数据清空,然后与新的文件相关联

5)【新建】【打开】菜单项的命令响应函数都在CWinApp类中,CWinApp有一个成员变量m_pDocManager,是指向CDocManager对象的指针,也就是说,CWinApp负责管理文档管理器,而后者有一个文档模板指针链表:m_templateList,用来保存文档模板的指针,即文档管理器负责管理文档模板,而后者又用来管理文档类、框架类和视类,始终让这三个对象三位一体,一起为文档服务

4.可串行化的类

1)如果要CArchive类保存一个对象的话,那么这个对象的类必须支持串行化,一个可串行化的类通常都有一个Serialize成员函数

2)使一个类可串行化:

     1.CObject或其派生类派生

     2.重写Serialize成员函数

     3.使用DECLARE_SERIAL宏(在声明),声明形式:DECLARE_SERIALclass_name

     4.定义不带参数的构造函数

     5.为类在实现文件中使用IMPLEMENT_SERIAL宏,该宏的声明如下:

 IMPLEMENT_SERIALclass_name,base_class_name,wSchema//类名,基类名,版本号

3)利用可串行化类的Serialize函数保存和加载对象

     1.集合类CObArray,该类用法和CPtrList相似,只是在添加元素时添加的时CObject指针 m_obArray.Add(pGraph);

     2.在文档类中想要访问视类的对象,首先要获得视类对象的指针,对于一个文档类来说,可以有多个视类对象与之相关,但对于一个视类对象来说,它只能与一个文档类对象相关,为了获得与文档对象相关的视类对象,首先要通过CDocument类的成员函数GetFirstViewPosition()获得与该文档对象相关的视类链表中第一个视类对象的位置,然后通过GetNextView获得当前位置所指示的视类对象指针

      函数原型:virtual POSITION GetFirstViewPosition() const;

      函数原型:virtual CView* GetNextView(POSITION& rPosition) const;

//该函数调用之后,将返回这个位置所标识的视类对象指针,然后通过rPosition参数返回下

//一个视类对象的位置,于是不断调用GetNextView函数,就可以得到与文档类对象相关的

//每一个视类对象,如果到了视类链表的末尾,rPosition的值就会被置为NULL

     3.有了视类对象的指针,就可以利用此指针访问视类对象的成员变量了

     4. 利用视类的成员变量CObArray m_obArray(保存obArray中存储的对象(可串行化类的对象)和将对象加载到obArray中)

     5.版本号:在利用文档类的Serialize函数保存一个可串行化类的对象时,实际上是利用对象本身的Serialize函数来完成的,因此,对象需要保存和读取的数据都在需要在该对象的Serialize函数中确定,这就要求在设计可串行化类的时候,在其内部确定需要串行化的数据,这就是可串行化类的内部实现机制

     6.利用CObArray类对串行化的支持保存和加载数据

1)由于CObArray类支持串行化,该类也有一个Serialize函数,CGraphicDoc对象的Serialize函数是由框架调用的,在该函数中,我们可以直接用它的参数arCArchive类型)传递给CObArray对象的Serialize函数(注意:一定要把这句调用代码放在if/else语句外面)pView->m_obArray.Serialize(ar);

2CObArray类的Serialize函数利用一个for循环将数组中的所有元素依次写入到文件中

3)若将CObArray定义到文档类,在视类中访问文档类的成员,首先要得到文档类的指针,在视类中有一个GetDocument函数,这个函数就是视类为我们准备的可以获取文档类指针的函数,它可以直接获得指向文档类对象的指针。

5.文档对象的销毁

    1)以上的程序中,我们在OnLButtonUp函数中在堆上为CGraph对象分配了内存,但没有释放(画完一个图形时,新建一个对象)当我们新建一个文档或打开一个文档时,程序文档对象所保存的数据要被销毁,然后再与一个新的文档相关联,然而对于在堆上分配的内存,必须由程序员自己去释放

2)因为文件打开和文件新建都会调用DeleteContents函数,所以在这个函数中释放文档对象在堆上分配的内存比较合适,DeleteContents函数是一个虚函数,主要由框架类调用,用来删除文档的数据,同时并不销毁CDocument对象本身,它是在文档将要被销毁之前被调用,它也会在该文档对象在重复使用之前被调用,以确保文档是空的。对单文档程序来说,文档对象是被重复使用的,所以在文档对象被重复使用之前,应释放已分配的内存。

3)为CGraphicDoc类添加DeleteContents虚函数,并添加代码

4)利用CObArray类的RemoveAt函数删除元素时,它会下移在这个元素之上的所有元素,并减少这个数组的上界

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值