13串行化

第13课 文档与串行化
1.CArchive在菜单打开保存时的代码
CFile file("1.txt",CFile::modeCreate | CFile::modeWrite);
CArchive ar(&file,CArchive::store);
int i=4;
char ch='a';
float f=1.3f;
CString str("http://www.sunxin.org");
ar<<i<<ch<<f<<str;以上是保存,打开略


2.文档-视类结构简介
   OnNewDocument在程序启动时被调用,此时可设置文档标题,也可以在String Table的IDR_MAINFRAME的第二个后改变文档的标题。须了解的7个字符串的用途,见PPT。
   在WinAPP的InitInstance()中完成DOC,View,MainFrame的归一。
   当点击系统的打开和新建菜单时,有一系列的步骤,孙鑫老师给我们跟踪了代码的调用过程,此段跟踪我们略过。但我们要牢记住:CWinAPP负责管理文档管 理器,文档管理器有一个指针链表,且来保存文档模板的指针,文档模板指针管理三个类DOC,VIEW,MAINFRAME,使其为某文件对象服务。


3.利用CArchive来保存一个类的对象,此类必须支持串行化,需要5个步骤。
  a.让类从CObject派生;
  b.覆盖Serialize()函数,在其中完成保存和读取功能;
  c.在.h中加入 DECLARE_SERIAL(CGraph);
  d.在。cpp中加入IMPLEMENT_SERIAL(CGraph, CObject, 1 );
  e.定义一个不带参数的构造函数。


保存绘画数据到文件的简单过程
  a.在CGraph中增加一个画图的成员函数,其实不增加也行。可以在View中完成相应功能。
  b.增加四个画图菜单,菜单可以从11课的代码中拷贝。
  c.在View中增加LButtonDown和UP的响应,在UP中画图,在DOWN中保存点
  d.利用CObArray集合类来保存绘画数据
  e.在CGraphicDOC::Serialize()中保存和读取数据
  f.然后在OnDraw中重绘。
4.新建和打开文档时,要注意销毁原来的数据。在DOC的DeleteContents虚函数中是好时机。代码如下
int nCount;
nCount=m_obArray.GetSize();


while(nCount--)
{
  delete m_obArray.GetAt(nCount);
  m_obArray.RemoveAt(nCount);
}
 
 
把文件在磁盘上保存下来 叫做串行化
可以使用CArchive:
写入:
 CFile file("1.txt",CFile::modeCreate | CFile::modeWrite);
 CArchive ar(&file,CArchive::store);
 int i=4;
 float b=1.3f;   //C默认用float定义而不加f的为double型
 CString str="SongPeng";
 ar<<i<<b<<str;
读取:
 CFile file("1.txt",CFile::modeRead);
 CArchive ar(&file,CArchive::load);
 int i;
 float b;
 CString str;
 CString strRestult;
 ar>>i>>b>>str;
 strRestult.Format("%d %f %s",i,b,str);
 MessageBox(strRestult);




在BOOL CGraphicDoc::OnNewDocument()中,我们可以设置文档,例如:
 SetTitle("SongPeng")
 
OnNewDocument()用来在程序运行之初构造文档,或是新建文档:
Called by the framework as part of the File New command. The default implementation of this function calls the DeleteContents member function to ensure that the document is empty and then marks the new document as clean. Override this function to initialize the data structure for a new document. You should call the base class version of this function from your override.




我们已可以在String Table中修改标题,其中的IDR_MAINFRAME中的字串原为:
Graphic\n\nGraphi\n\n\nGraphic.Document\nGraphi Document
各子串之间用\n分割,可以有空子串。
各子串定义如下:
CDocTemplate::windowTitle   Name that appears in the application window’s title bar (for example, “Microsoft Excel”). Present only in the document template for SDI applications.


CDocTemplate::docName   Root for the default document name (for example, “Sheet”). This root, plus a number, is used for the default name of a new document of this type whenever the user chooses the New command from the File menu (for example, “Sheet1” or “Sheet2”). If not specified, “Untitled” is used as the default.


CDocTemplate::fileNewName   Name of this document type. If the application supports more than one type of document, this string is displayed in the File New dialog box (for example, “Worksheet”). If not specified, the document type is inaccessible using the File New command.


CDocTemplate::filterName   Description of the document type and a wildcard filter matching documents of this type. This string is displayed in the List Files Of Type drop-down list in the File Open dialog box (for example, “Worksheets (*.xls)”). If not specified, the document type is inaccessible using the File Open command.


CDocTemplate::filterExt   Extension for documents of this type (for example, “.xls”). If not specified, the document type is inaccessible using the File Open command.


CDocTemplate::regFileTypeId   Identifier for the document type to be stored in the registration database maintained by Windows. This string is for internal use only (for example, “ExcelWorksheet”). If not specified, the document type cannot be registered with the Windows File Manager.


CDocTemplate::regFileTypeName   Name of the document type to be stored in the registration database. This string may be displayed in dialog boxes of applications that access the registration database (for example, “Microsoft Excel Worksheet”).
在前两个\n之间加入字符串,就达到修改标题的效果。
在新建工程的第四部advance选项中也能进行修改
凡是从CCmdTarget派生出的类,都可以间接接受命令消息


新建文档运行路径:
void CWinApp::OnFileNew()--->void CDocManager::OnFileNew()---> 
CDocTemplate* pTemplate = (CDocTemplate*)m_templateList.GetHead();--->
pTemplate->OpenDocumentFile(NULL);--->CDocument*
CSingleDocTemplate::OpenDocumentFile(LPCTSTR lpszPathName,
 BOOL bMakeVisible)--->pDocument = CreateNewDocument();
--->pFrame = CreateNewFrame(pDocument, NULL);--->
if (!pDocument->OnNewDocument())--->BOOL CGraphicDoc::OnNewDocument()
CWinApp负责管理文档管理器
打开文档运行路径
void CWinApp::OnFileOpen()--->void CDocManager::OnFileOpen()--->
DoPromptFileName--->BOOL CDocManager::DoPromptFileName(CString& fileName, UINT nIDSTitle, DWORD lFlags, BOOL bOpenFileDialog, CDocTemplate* pTemplate)--->打开一个文件 --->AfxGetApp()->OpenDocumentFile(newName);--->
CDocument* CWinApp::OpenDocumentFile(LPCTSTR lpszFileName)--->
CDocument* CDocManager::OpenDocumentFile(LPCTSTR lpszFileName)--->
match = pTemplate->MatchDocType(szPath, pOpenDocument);--->
判断pOpenDocument是否为空,pOpenDocument用来指定是否与先前的文档关联,即获取先前相同文档的指针,如果是,不再对文档进行操作---->
CDocument* CSingleDocTemplate::OpenDocumentFile(LPCTSTR lpszPathName,
 BOOL bMakeVisible)--->if (pDocument == NULL)--->if (pFrame == NULL)--->
if (lpszPathName == NULL)--->if (!pDocument->OnOpenDocument(lpszPathName))--->
BOOL CDocument::OnOpenDocument(LPCTSTR lpszPathName)---->
Serialize(loadArchive);---->void CGraphicDoc::Serialize(CArchive& ar)
生成可串行化的类:
 A serializable class usually has a Serialize member function, and it usually uses the DECLARE_SERIAL and IMPLEMENT_SERIAL macros, as described under class CObject.
分为五步:
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.
IMPLEMENT_SERIAL(CGraph,CObject,1),1为版本号,保存和读取时版本号必须相同
一个文档类对象能和多个视类对象相关,一个视类对象只和一个文档类对象相关.
单文档类只有一个视类对象,要获取视类对象的指针
要在文档类中访问定义在视类中的对象,首先要获得视类的指针.需要用到函数
CDocument::GetFirstViewPosition 得到视类对象的位置
virtual POSITION GetFirstViewPosition( ) const;
A POSITION value that can be used for iteration with the GetNextView member function.
Call this function to get the position of the first view in the list of views associated with the document.
然后再调用GetNextView得到视类对象的指针
 POSITION pos=GetFirstViewPosition();
 CGraphicView *pView=(CGraphicView *)GetNextView(pos);
POSITION   A value used to denote the position of an element in a collection; used by MFC collection classes.
我们文档类中调用Serialize保存一个可串行化的类的数据时实际上是利用了这个对象本身的Serialize函数,这个对象需要什么对象,都需要在你编写可串行化的类时去确定
void CGraph::Serialize(CArchive &ar)
{
 if(ar.IsStoring())
 {
  ar<<m_nDrawType<<m_pOrigin<<m_pEnd;
 }
 else
 {
  ar>>m_nDrawType>>m_pOrigin>>m_pEnd;
 }
}
void CGraphicDoc::Serialize(CArchive& ar)
{
 POSITION pos=GetFirstViewPosition();
 CGraphicView *pView=(CGraphicView *)GetNextView(pos);
 if (ar.IsStoring())
{
  int nCount=pView->m_obArray.GetSize();
  ar<<nCount;
  for(int i=0;i<nCount;i++)
  {
   ar<<pView->m_obArray.GetAt(i);
  }
 }
 else
 {
  int nCount;
  ar>>nCount;
  CGraph *pGraph;
  for(int i=0;i<nCount;i++)
  {
   ar>>pGraph;
   pView->m_obArray.Add(pGraph);
  }
 }
}
数组的保存:
 CGraph *pGraph=new CGraph(m_nDrawType,m_pOrigin,point);
 m_obArray.Add(pGraph);
CObArray incorporates the IMPLEMENT_SERIAL macro to support serialization and dumping of its elements. If an array of CObject pointers is stored to an archive, either with the overloaded insertion operator or with the Serialize member function, each CObject element is, in turn, serialized along with its array index
void CGraphicDoc::Serialize(CArchive& ar)可被修改为:
void CGraphicDoc::Serialize(CArchive& ar)
{
 POSITION pos=GetFirstViewPosition();
 CGraphicView *pView=(CGraphicView *)GetNextView(pos);
 if (ar.IsStoring())
{
}
else
{
}
pView->m_obArrary.Serialize(ar);
}
以下是CObArray在MFC中的源代码:
void CObArray::Serialize(CArchive& ar)
{
 ASSERT_VALID(this);
 CObject::Serialize(ar);
 if (ar.IsStoring())
 {
  ar.WriteCount(m_nSize);
  for (int i = 0; i < m_nSize; i++)
   ar << m_pData[i];
 }
 else
 {
  DWORD nOldSize = ar.ReadCount();
  SetSize(nOldSize);
  for (int i = 0; i < m_nSize; i++)
   ar >> m_pData[i];
 }
}
文档类时用来管理数据的我们把CObArray m_obArray放入文档类中
以下为自动生成类,这是在View类中准备好的可以获得文档来指针的函数:
CGraphicDoc* CGraphicView::GetDocument() // non-debug version is inline
{
 ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CGraphicDoc)));
 return (CGraphicDoc*)m_pDocument;
}
 在View中可以这样调用m_obArray
CGraphicDoc *pDoc=GetDocument();
 pDoc->m_obArray.Add(pGraph);
Document/View结构
在MFC中,文档类负责管理数据,提供保存和数据加载的功能。视类负责数据的显示,以及给用户提供对数据的编辑和修改功能。
MFC 给我们提供Document/View结构,将一个应用程序所需要的数据处理与显示的函数的空壳都设计好了,这些函数都是需函数,我们可以在派生类中重写 这些函数。有关文件读写的操作在CDocument的Serialize函数中进行,有关数据和图形显示的操作在CView的OnDraw函数中进行。我 们在其派生类中,只需要去关注serialize和OnDraw函数就可以了,其他的细节我们不需要去理会,程序就可以良好的运行。
当我么按下"File/Open",Application Framework会激活文件打开对话框,让你指定文件名,然后自动调用CGraphicView::OnDraw,传递一个显示DC,让你重新绘制窗口内容。
MFC给我们提供Document/View结构,是希望我们将精力放在数据结构的设计和数据显示的操作上,而不是把时间和精力花费在对象与对象之间,模块与模块之间的通信上。
一个文档对象可以和多个视类对象相关联,而一个视类对象只能和一个文档类对象相关联
不管是新建文档还是打开另外一份文档,都会调用DeletContents,以保证新建文档时空的
释放对内存
void CGraphicDoc::DeleteContents()
{
 // TODO: Add your specialized code here and/or call the base class
 int nCount;
 nCount=m_obArray.GetSize();
 for(int i=0;i<nCount;i++)
 {
  delete m_obArray.GetAt(i);  //解除指针与值之间的联系,但没有清除堆内存
  m_obArray.RemoveAt(i);  //清除堆内存
 }
 CDocument::DeleteContents();
}
CDocument::DeleteContents()会在打开,新建,关闭文档时被调用
CObArray::RemoveAt
Removes one or more elements starting at a specified index in an array. In the process, it shifts down all the elements above the removed element(s). It decrements the upper bound of the array but does not free memory.RemoveAt会导致数组中数据的重排
因此,需要把以上代码修改为
void CGraphicDoc::DeleteContents()
{
 // TODO: Add your specialized code here and/or call the base class
 int nCount;
 nCount=m_obArray.GetSize();
 for(int i=0;i<nCount;i++)
 {
  delete m_obArray.GetAt(i);
 }
 m_obArray.RemoveAll();
 CDocument::DeleteContents();
}
另一种方法
 while(nCount--)
 {
  delete m_obArray.GetAt(nCount);
  m_obArray.RemoveAt(nCount);
 }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值