MFC 用串行化实现文档存储和读取

        在面向对象的程序设计中,一般都是用二进制文件来保存文档资料。在VC++中控制和使用文件流的方法很多,MFC程序设计中常用的有两种方法:用CFile对象存储和读取文件;利用串行化存取文件。其中用CFile对象直接存储文档,存在着以下两个问题:一是过程繁琐,例如绘图系统中常存在大量的数据对象(直线对象、矩形对象等);另一个问题是功能受限,VC++为了程序集成的需要,设汁成了复合文档,可以把各种外来对象(如OLE对象的嵌入和链接)的内容存储到外部文件中并从外部文件中读取内容更新构造对象,而程序的设计者并不知道这些对象中需要存储数据的内容和格式,在这种情况下,直接利用CFile类就无能为力了。

      既然已决定采用串行化的方法,就要明确串行化的概念:CArchive类用于以持久的二进制形式(通常是磁盘存储)来存储一个复杂的对象网络,用户可以把对象的内容存储到存储区中,也可以从存储区中读取内容重新构造对象。这个过程成为“串行化”,下面介绍串行化的几个基础知识点:

 


 

     1 CArchive。构造函数

      CArchive( CFile* pFile, UINT nMode, int nBufSize = 4096, void* lpBuf = NULL ),参数意义查阅MSDN。两个重要函数:IsStoring()和IsLoading(),以及>>和<<两个重载的操作符。

 

     2 如何使自定义的类具有串行化功能  有以下五个步骤:

 

     1)Deriving your class from CObject (or from some class derived from CObject).

     2)Overriding the Serialize member function.

     3)Using the DECLARE_SERIAL macro in the class declaration. 

     4)Defining a constructor that takes no arguments.

     5)Using the IMPLEMENT_SERIAL macro in the implementation file for your class.

 

     3 CObArray::Serialize()。

     CObArray类支持CObject类对象指针数组,它如同一个指针数组来管理直接或间接派生的CObject对象指针。当调用一个CObArray对象的Serialize()函数进行存储操作时,首先存储了数组的项数,然后循环对各个项目(各个对象)进行存储,进行了如下操作:

 

      1)将对象的信息写入文件。包括类的信息;版本信息;

      2)调用对象的Serialize成员函数,将对象的数据写入文件。

      而当进行读取操作时,首先读取了存储数组的项目数,然后将数组的项目初始化成需要的项目数,然后进行如下操作:

      1)从文件读取类的信息,动态生成相应类(CLine)的对象,存放对象的指针;

      2)调用对象的Serialize()成员函数,从文件读取对象数据初始化新生成的对象。

 

      4Document/View结构.

 

      1)在MFC中,文档类负责管理数据,提供保存和加载数据的功能。视类负责数据的显示,以及给用户提供对数据的编辑和修改功能。
      2)MFC给我们提供Document/View结构,将一个应用程序所需要的“数据处理与显示”的函数空壳都设计好了,这些函数都是虚函数我们可以在派生类中重写这些函数。有关文件读写的操作在CDocument的Serialize函数中进行,有关数据和图形显示的操作在CViewOnDraw函数中进行。我们在其派生类中,只需要去关注Serialize和OnDraw函数就可以了,其它的细节我们不需要去理会,程序就可以良好地运行。
      3)当我们按下“File/Open”,Application Framework会激活文件打开对话框,让你指定文件名,然后自动调CGraphicDoc::Serialize读取文件。Application Framework还会调用CGraphicView::OnDraw,传递一个显示DC,让你重新绘制窗口内容。MFC给我们提供Document/View结构,是希望我们将精力放在数据结构的设计和数据显示的操作上,而不要把时间和精力花费在对象与对象之间、模块与模块之间的通信上。
      4)一个文档对象可以和多个视类对象相关联,而一个视类对象只能和一个文档对象相关联。

 

      5 String Table中IDR_MAINFRAME字符串资源中各子串的含义.

 

      1)CDocTemplate::windowTitle,主窗口标题栏上的字符串,MDI程序不需要指定,将以IDR_MAINFRAME字符串为默认值。
      2)CDocTemplate::docName,缺省文档的名称。如果没有指定,缺省文档的名称是无标题。
      3)CDocTemplate::fileNewName,文档类型的名称。如果应用程序支持多种类型的文档,此字符串将显示在"File/New"对话框中。如果没有指定,就不能够在"File/New"对话框处理这种文件。
      4)CDocTemplate::filterName,文档类型的描述和一个适用于此类型的通配符过滤器。这个字符串将出现在“File/Open”对话框中的文件类型列表框中。要和CDocTemplate::filterExt一起使用。
      5)CDocTemplate::filterExt,文档的扩展名。如果没有指定,就不能够在“File/Open”对话框中处理这种文档。要和CDocTemplate::filterName一起使用。
      6)CDocTemplate::regFileTypeId,如果你以::RegisterShellFileTypes向系统的注册表注册文件类型,此值会出现在HEY_CLASSES_ROOT之下成为其子项,并仅供Windows内部使用。如果没有指定,这种文件类型就无法注册。
      7)CDocTemplate::regFileTypeName,这也是存储在注册表中的文件类型名称。它会显示于程序中用以访问注册表的对话框内。

     


 

      下面介绍用串行化实现文档存储与读写的步骤:

      1 实现一个可串行化的类(五个步骤)。特别注意的是改写Serialize()函数;

 

      2 修改String Table中IDR_MAINFRAME字符串资源中子串,使其符合自己需求;

 

      3 在CMyDoc类中增加一个CObArray的成员变量;修改CMyDoc::Serialize();如果需要保存CMyView类中的数据成员,可以利用一下代码获取CMyView的指针:POSITION pos=GetFirstViewPosition(); CGraphicView *pView=(CGraphicView*)GetNextView(pos);

 

     4 新建和打开文档时,要注意销毁原来的数据。在DOC的DeleteContents虚函数中是好时机。代码如下:
             int nCount;
             nCount=m_obArray.GetSize();
             for(int i=0;i<nCount;i++)
             {
                 delete m_obArray.GetAt(i);//释放指针指向的内存空间
                //m_obArray.RemoveAt(i);//移除链表中的元素。嘿嘿,别搞错了。但在此处不能这样用,会导致非法操作。要用下面的方法

             }
         m_obArray.RemoveAll();

      还有一种方法:
              while(nCount--)
              {
                        delete m_obArray.GetAt(nCount);
                        m_obArray.RemoveAt(nCount);
              }

 

 


 

一、MFC允许对象在程序运行的整个过程中持久化的串行化机制

(1)串行化是指向持久化存储媒介(如一个磁盘文件)读或写对象的过程。

(2)串行化用于在程序运行过程时或之后修复结构化数据(如C++类或结构)的状态。

(3)MFC支持CObject类中的串行化,所以,所有继承于CObject的类可以利用CObject的串行化协议。

(4)串行化的基本思想:

          a、对象必须能将其当前状态写入到持久化存储媒介中,通常用其成员变量实现。

          b、对象可以通过读或反序列化从存储媒介中重新构造对象的状态。

          c、串行化处理所有对象指针的细节,以及序列化对象时对对象的循环引用。

          d、关键点是对象自己负责读和写其本身的状态,所以,序列化一个对象时,必须是想基本的序列化操作。

(5)MFC使用CArchive类的对象作为被序列化的对象和存储媒介之间的中间媒介。

二、生成一个可串行化的类的步骤

(1) Derive your class from CObject. (定义一个基类为CObject的类)

(2) Override the Serialize member function.(重写串行化函数)

(3) Use the DECLARE_SERIAL macro in the class declaration.(在类声明文件中使用DECLARE_SERIAL宏)

(4) Define a constructor with no arguments (a default constructor).(定义一个无参数的构造函数)

(5) Use the IMPLEMENT_SERIAL macro in the class implementation file.(在实现文件中使用IMPLEMENT_SERIAL宏)

三、实例

Graph.h文件:

  1. #pragma once  
  2.   
  3. // Graph 命令目标  
  4.   
  5. class Graph : public CObject  //(1)定义一个基类为COject的类  
  6. {  
  7.     DECLARE_SERIAL(Graph)  //(3)在类声明文件中使用DECLARE_SERIAL宏  
  8. public:  
  9.     Graph(); //(4)定义一个无参数的构造函数  
  10.     Graph(int drawType, CPoint ptOld);  
  11.     virtual ~Graph();  
  12.   
  13.     void Serialize(CArchive &ar); //(2)重写串行化函数  
  14.   
  15. private:  
  16.     int m_drawType;  
  17.     CPoint m_ptOld;  
  18. };  

Graph.cpp文件:

  1. // Graph.cpp : 实现文件  
  2. //  
  3.   
  4. #include "stdafx.h"  
  5. #include "Archive.h"  
  6. #include "Graph.h"  
  7.   
  8.   
  9. // Graph  
  10.   
  11. IMPLEMENT_SERIAL(Graph, CObject, 1) //(5)在实现文件中使用IMPLEMENT_SERIAL宏  
  12.   
  13. Graph::Graph() //(4)定义一个无参数的构造函数  
  14. {  
  15. }  
  16.   
  17. Graph::Graph(int drawType, CPoint ptOld)  
  18. {  
  19.     this->m_drawType = drawType;  
  20.     this->m_ptOld = ptOld;  
  21. }  
  22.   
  23. Graph::~Graph()  
  24. {  
  25. }  
  26.   
  27. // Graph 成员函数  
  28. void Graph::Serialize(CArchive &ar) //(2)重写串行化函数  
  29. {  
  30.     if (ar.IsStoring())  
  31.     {  
  32.         ar<<m_drawType<<m_ptOld;  
  33.     }  
  34.     else  
  35.     {  
  36.         ar>>m_drawType>>m_ptOld;  
  37.     }  
  38. }  



本文来自

lqlong19922008的专栏

http://blog.csdn.net/lqlong19922008/archive/2010/09/08/5870142.aspx

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值