文档和串行化
让对象数据持久性的过程就称为串行化。我们用CArchive类达到这个目的。可以把CArchive类对象想象成一种二进制流,像C++中的输入流和输出流一样。一个CArchive对象与个文件相关,并允许带缓存机制的数据写入和读出。输入输出流处理的是ASCII码字符序列,而一个CArchive对象以一种有效的、非冗余的格式处理二进制对象数据。
构造了一个CArchive对象后,不可以将它与一个代表某个打开文件的CFile类对象或其派生类对象相关联,一个文件只能与一个活动的存档对象相关联。
CArchive( CFile* pFile, UINT nMode, int nBufSize = 4096, void* lpBuf = NULL );
CArchive重载了>>,<<,可以支持基本类型和CObject类的派生类。
friend CArchive& operator >>( CArchive& ar, CObject *& pOb );//流入变量中
friend CArchive& operator >>( CArchive& ar, const CObject *& pOb );//流出到文档类对象中
对象保存的顺序和提取的顺序必须一致。
1、MFC框架程序提供的文件新建功能
OnNewDocument是框架调用的一个虚函数,是文件/新建的一部分。可以在其中修改文档的标题,SetTitle。也可以在字符串资源IDR_MAINFRAME资源中修改,在BOOL CMyFileApp::InitInstance()中将字符串资源传递给了程序的框架。
IDR_MAINFRAME是由\n相隔的7个子串。
CSingleDocTemplate* pDocTemplate;
pDocTemplate = new CSingleDocTemplate(
IDR_MAINFRAME,
RUNTIME_CLASS(CMyFileDoc),
RUNTIME_CLASS(CMainFrame), // 主SDI 框架窗口
RUNTIME_CLASS(CMyFileView));
if (!pDocTemplate)
return FALSE;
AddDocTemplate(pDocTemplate);
可以用文档模板类的GetDocString成员函数来获取各个子串。像上面这样构造文档,并用AddDocTemplate加入到文档模板链中。在利用向导的第四步中,有“文档模板字符串”,其实也在设置这个。
virtual BOOL GetDocString( CString& rString, enum DocStringIndex index ) const;
第二个参数的7个参量,就是IDR_MAINFRAME的对应7个量。
MyFile\nGraphic\nFile\nText Files(*.txt)\n.txt\nGraphic.Document\nGraphi Document
afx_msg标识符,表明函数是一个菜单命令消息响应函数。在CDocManager类内部有一个CPtrList类型的指针链表维护了一系列的文档模板指针。
2、OnNewDocument函数的调用过程
MFC提供的文档/视类结构有一个特点,每当有一份文档产生时,总是会产生一个文档类对象、框架类对象和视类对象,它们三位一体为这份文档服务。
当程序运行时,MFC框架内部接受的无论是文档类指针、框架类指针、或者是视类指针,它们都是指向派生类的指针。
凡是从CCmdTarget类派生的类都可以接收命令消息。
15、文档和串行化 - EdwardLewis - 墨涵天地
15、文档和串行化 - EdwardLewis - 墨涵天地
3、文档串行化
文档类的Serialize函数就是文档类提供的用来保存和加载数据的函数。MFC提供的文档/视类结构,相当于把将数据的处理和显示功能划分为不同的模块来完成。
15、文档和串行化 - EdwardLewis - 墨涵天地
[文件\新建]和[文件\打开]菜单项的命令响应函数都是在CWinApp类中提供。CWinApp类有1个成员变量:m_pDocManager,是指向CDocManager对象的指针,即,CWinApp负责管理文档管理器,而后者有一个文档模板指针链表:m_templateList,用来保存文档模板的指针,即文档管理器负责管理文档模板,而后者又是用来管理文档类、框架类和视类的,始终让这3个对象三位一体,一起为文档服务。
4、可串行化的类
用一个CArchive类保存一个对象的话,那么这个对象的类必须支持串行化。
(1)从CObject派生类(或从CObject派生的某个类派生)
(2)重写Serialize成员函数
(3)使用DECLARE_SERIAL宏(在类声明中)。
DECLARE_SERIAL(class_name)
(4)定义不带参数的构造函数。
(5)为类在实现文件中使用IMPLEMENT_SERIAL宏。
IMPLEMENT_SERIAL( class_name, base_class_name, wSchema )
用CObArray类对象来保存,加载对象(如图形),他就像一个容器。
对于一个文档类对象来说,可以有多个视类对象与之相关。但是对于一个视类对象来说,它只能与一个文档类对象相关。
CDocument::GetFirstViewPosition
virtual POSITION GetFirstViewPosition( ) const;
A POSITION value that can be used for iteration with the member function.
Call this function to get the position of the first view in the list of views associated with the document.
CDocument::GetNextView
virtual CView* GetNextView( POSITION& rPosition ) const;
return A pointer to the view identified by rPosition.
Call this function to iterate through all of the document’s views. The function returns the view identified by rPosition and then sets rPosition to the POSITION value of the next view in the list. If the retrieved view is the last in the list, then rPosition is set to NULL.
void CMyDoc::OnRepaintAllViews()
{
POSITION pos = GetFirstViewPosition();
while (pos != NULL)
{
CView* pView = GetNextView(pos);
pView->UpdateWindow();
}
}
对象需要保存和读取的数据都需要在该对象的Serialize函数中确定。在设计可串行化的类时,在其内部确定需要串行化的数据。
CObArray类支持串行化。CGraphicDoc对象的Serialize函数是由框架调用的,我们可以在其中调用CObArray的S函数。
CMyFileDoc* GetDocument() const;
CView::GetDocument
CDocument* GetDocument( ) const;
A pointer to the object associated with the view. NULL if the view is not attached to a document.
5、总结一下
在MFC中,文档类负责管理数据,提供保存和加载数据的功能。视类负责数据的显示,以及给用户提供对数据的编辑和修改功能。
MFC给我们提供Document/View结构,将一个应用程序所需要的“数据处理与显示”的函数空壳都设计好了,这些函数都是虚函数,我们可以在派生类中重写这些函数。有关文件读写的操作在CDocument的Serialize函数中进行,有关数据和图形显示的操作在CView的OnDraw函数中进行。我们在其派生类中,只需要去关注Serialize和OnDraw函数就可以了,其它的细节我们不需要去理会,程序就可以良好地运行。
当我们按下“File/Open”,Application Framework会激活文件打开对话框,让你指定文件名,然后自动调用CGraphicDoc::Serialize读取文件。Application Framework还会调用CGraphicView::OnDraw,传递一个显示DC,让你重新绘制窗口内容。
MFC给我们提供Document/View结构,是希望我们将精力放在数据结构的设计和数据显示的操作上,而不要把时间和精力花费在对象与对象之间、模块与模块之间的通信上。
一个文档对象可以和多个视类对象相关联,而一个视类对象只能和一个文档对象相关联。
6、文档对象的销毁
在CDocument类中的OnNewDocument函数中来进行文档对象的删除。不管是新建文档,还是打开一个已有文档,都是先删除数据。
DeleteContents函数是一个虚函数,主要是由框架调用,用来删除文档的数据,同时并不销毁CDocument对象本身。它是在文档将要被销毁前被调用,它也会在该文档对象在重复使用之前被调用,以确保文档是空的。所以我们只要得写这个函数就可以达到删除先前保存数据的删除。
注意RemoveAll,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.RemoveAt(i);
}
m_obArray.RemoveAll();*/
while(nCount--)
{
delete m_obArray.GetAt(nCount);
m_obArray.RemoveAt(nCount);
}
CDocument::DeleteContents();
}
chap8_2 文档和串行化
最新推荐文章于 2024-07-24 14:00:15 发布