?? 13.4.2 利用可串行化類的 serialize函數(shù)保存和加載對象.txt
字號:
13.4.2 利用可串行化類的 Serialize函數(shù)保存和加載對象
下面,在 CGraphic View類的 OnLButtonUp函數(shù)中,當(dāng)繪制圖形之后,將圖形要素保存起來,在前
面第 11章中使用集合類: CPtrList來實現(xiàn),這電使用另一個類=CObArTay來實現(xiàn),該類的用法與
CPtrList非常類似,只是在添加元素時添加的是 CO同 ect指針。因為 CGraph類就是從 CObject類
派生的,所以使用 CObArray類比較合適。為了保存多個圖形要素,為 CGraphicView類添加一個
CObArray類型的成員變量 : ffi_obArray,并將其訪問權(quán)限設(shè)置為 public類型,因為隨后在文檔類
中需要訪問這個變量。
另外,為了簡單起見,不再設(shè)置畫筆的顏色,所以將此函數(shù)中己有的構(gòu)造 CPen對象的代碼,以及將
此對象選入設(shè)備描述表中的代碼注釋起來。接著,在繪制圖形之后,添加如例 13-21所示代閨中加
灰顯示的代碼,先構(gòu)造 CGraph對象,然后將該對象添加到集合類中。
例 13.21
void CGraphicView: :OnLButtonUp(UINT nFlags , CPoint point)
{
// TODO : Add your message handler code here and/or call default
CClientDC dc(this) ;
// CPen pen(m_nL工 neStyle, m_nLineWidth , m_clr) ;
// dc . SelectObject(&pen);
CBrush
*pBrush=CBrush: : FromHandle( (HBRUSH)GetStockObject(NULL_BRUSH)); dc .
SelectObject(pBrush) ; switch(m_nDrawType) {
case 1: dc.SetPixel(point , m_clr); break;
case 2: dC.MoveTo(m-ptOrigin) ; dc.LineTo(point) ; break;
case 3: dc.Rectangle(CRect(m-ptOrigin , point)) ; break;
case 4: dc.Ellipse(CRect(m-ptOrigin , point)) ; break;
CGraph *pGraph=new CGraph(m_nDrawType,m-ptOrigin, point); ( m_obArray.Add(pGraph) ;
Cview : :OnLButtonUp(nFlags , point);
另外,在 CGraphicView類中如果想要使用 CGraph類型的對象,那么必須將該對象定
義所在的頭文件包含進(jìn)來,因此,在 GraphicView.cpp文件的前部添加下面這條語句:
#include "Graph .h "
接下來,在 CGraphicDoc類的 Serialize函數(shù)中,將 CGraphicView對象的集合類對象: m_obArray
中保存的圖形元素寫入到文件中。首先我們需要將該函數(shù)中先前添加的代碼注釋起來。另外,在文
檔類中想要訪問視類的對象,首先就要獲得視類對象的指針。我們知道,對于一個文檔類對象來說,
可以有多個視類對象與之相關(guān)。但對于一個視類對象來說,它只能與一個文檔類對象相關(guān)。因此,
為了獲得與文檔對象相關(guān)的視類對象,首先就要通過 CDocument類的 GetFirs tView Pos iti o n
成員函數(shù)獲得與該文檔對象相關(guān)的視類鏈表中第一個視類對象的位置,然后通過 GetNextView函數(shù)
得到當(dāng)前位置所指示的視類對象指針。 CDocument類的 GetFirst ViewPosition成員函數(shù)的原型聲
明如下所示 :
virtual POSITION GetFirstViewPosition( ) const ;
該函數(shù)將返回與文檔相關(guān)聯(lián)的視類對象鏈表中的第一個視類對象的位置,這個位置可以被
GetNextView函數(shù)選代使用。返回值類型是 POSOTION,這種類型被 MFC中的集合類用來表示集合中
元素的位置。
GetNextView函數(shù)的原型聲明如下所示 :
virtual CView* GetNextView( POS工T工ON& rPosition ) const ;
可以看到, GetNextView函數(shù)有一個 POSITION類型的參數(shù) : rPosition。當(dāng)調(diào)用 GetFirstViewPo s
ition函數(shù)之后就可以獲得第一個視類對象的位置,然后將這個值作為參數(shù)傳遞給 GetNextVie w函
數(shù),該函數(shù)調(diào)用之后,將返回這個位置所標(biāo)識的視類對象指針,然后通過 rPosition參數(shù)返回下一
個視類對象的位置。于是,通過不斷地調(diào)用 GetNextVie w函數(shù),就可以得到與文檔類對象相關(guān)的每
一個視類對象。如果到了視類鏈表的末尾,即沒有下一個視類對象了, rPo s ition的值就會被設(shè)
置為 NULL。因此程序中就可以根據(jù)這個條件,終止調(diào)用 GetNextView函數(shù)。通過這種方法可以迭代
地訪問與文檔對象相關(guān)的每一個視類對象。因為本程序是一個單文檔類型的程序,所以它只有一個
視類對象。所以在 CGraphicDoc類的 Serialize函數(shù)中開始位置添加如例 13-22所示代碼中加灰顯
示的代碼。
例 13 -22
void CGraphicDoc : : Serialize(CArchive& ar)
POSiTION pos=GetFirstViewPosition() ;
CGraphicView *pView= (CGraphicView*)GetNextView(pos) ;
if (ar . IsStoring())
// TODO: add storing code here
inti=5 ;
char ch='b' ;
float f =1. 2f;
CString str ( "http://www . sunxin. org") ;
ar <<i <<ch<<f<<str;
else
11 T ODO : add lo ad工 ng code here
int i:
char ch:
float f:
CStrlng str:
CString strResult:
a r>>i>>ch>>f>>str:
strResult .Forrnat( "屯d,氈c,氈f.氈s ", i , ch , f. str) :
Af xMessageBox (strResult) :
上述如例 13-22所示代碼通過調(diào)用 GetFirstViewPosition函數(shù)和 GetNextView函數(shù)獲取與文檔對
象相關(guān)的第一個視類對象的指針,也就是 CGraphicView對象的指針。要注意的是. GetNextView函
數(shù)返回的是 CView*.而我們需要的是 CGraphicView飛因此需要進(jìn)行一個強制類型轉(zhuǎn)換。
有了視類對象指針,就可以利用此指針訪問視類對象的成員變量了。于是在 CGraphicDoc類的
Serialize函數(shù)中,在保存數(shù)據(jù)時,得到 CGraphicView對象的 ID_obArray這個集合類對象中保存的
對象數(shù)目,然后利用一個 for循環(huán)結(jié)構(gòu)遍歷這些元素,分別將它們保存到文件中。但是,如果 for
循環(huán)語句用下面這條代碼來實現(xiàn):
for(int i=0;i<pView->m_obArray.GetSize();i++)
那么,每次循環(huán)時都會調(diào)用 GetSize函數(shù)去求取集合類對象中元素的數(shù)目,這樣會影
響程序的執(zhí)行效率。我們在編寫代碼時,應(yīng)該注意代碼優(yōu)化問題。這里,我們可以把 GetSize這個
函數(shù)調(diào)用提取出來,單獨定義一個變量來保存該函數(shù)調(diào)用后得到的集合類對象元素數(shù)目。如果要提
取己保存到文件中的數(shù)據(jù),首先需要知道該文件中保存的對象數(shù)目。所以在保存集合類對象所保存
的圖形對象時,可以在保存具體的對象數(shù)據(jù)之前,先將所要保存的對象數(shù)目也保存到文件中。接下
來,在 for循環(huán)內(nèi)部,利用 CArchive對象保存集合類對象中所保存的圖形對象。因為 CGraph類本
身支持串行化,所以可以直接保存該類對象。于是,我們可以在例 13-22所示 CGraphicDoc類
Serialize函數(shù)的 if語句塊中添加下述代碼。
int nCount=pView->m_obArray.GetSize();
ar<<nCount;
for(int i=0;i<nCount;i++)
ar<<pView->m_obArray.GetAt(i) ;
在讀取對象數(shù)據(jù)時,可以先取出對象數(shù)據(jù),然后,也利用 for循環(huán)語句來讀取所有對象的數(shù)據(jù)。每
次取出一個 CGraph對象之后,都可以將這個對象的地址加入到 CGraphic View 對象的 ID_obArray
這個集合類對象中。于是,我們可以在如例 13-22所示 CGraphicDoc類
Serialize函數(shù)的else分支下添加下述代碼:
int nCount;
ar>>nCount;
CGraph *pGraph;
for(int i=0;i<nCount;i++)
ar>>pGraph;
pView->rn_obArray.Add(pGraph) ;
注意,這里并沒有先構(gòu)造一個CGraph對象,再讀取其數(shù)據(jù)。因為這里在利用CArchive類重載的提取
操作符 C>>)讀取對象時,它會自動調(diào)用CGraph類的不帶參數(shù)的構(gòu)造函數(shù)去構(gòu)造相應(yīng)對象,然后將這
個對象的地址賦給 pGraph這個指針變量。所以這里并不需要
為該指針變量分配內(nèi)存空間。
在CGraphicDoc類中若想要訪問CGraphicView類和CGraph類,需要包含相應(yīng)的頭文件,即在
GraphicDoc.cpp文件的前部添加下述語句:
#include "GraphicView.h"
#include "Graph.h"
在提取文件數(shù)據(jù)時,當(dāng)我們將CGraph對象添加到集合類對象之后,還需要將這些圖形在程序窗口中
顯示出來。這可以在 CGraphicView類的 OnDraw函數(shù)中把圖形的重繪工作完成了。因為在 CGraph
類中已經(jīng)包含了圖形繪制函數(shù) (Draw),所以這里只需要調(diào)用這個函數(shù)就可以。于是,在CGraphicView
類的OnDraw函數(shù)中添加如例 13-23所示代碼中加灰顯示的代碼。
例13-23
void CGraphicView: : OnDraw(CDC食 pDC)
{ CGraphicDoc* pDoc = GetDocurnent(); ASSERT_VAL工o (pDoc) ; // TODO: add draw code for
native data here
/ * CFont *pOldFont=pDC->SelectObject(&rn_font);
pDC->TextOut(Q , Q, rn_strFontNarne) ;
pDC->SelectObject(pOldFont) ;
*/
int nCount;
nCount=m_obArray.GetSize() ;
for(int i=0 ,i<nCount ,i++)
{
((CGraph*)m_obArray.GetAt(i))->Draw(pDC) ;
新添加的代碼通過調(diào)用集合類對象的GetAt函數(shù)獲取元素,并需要將結(jié)果強制轉(zhuǎn)換為 CGrath*類型,
然后利用此指針調(diào)用 CGraph對象的 Draw函數(shù)完成圖形的繪制。而調(diào)用
" I 513
第 13章文檔與串行化
Draw函數(shù)時,需要傳遞一個 CDC*類型的參數(shù),正好 CGraphicView類的 OnDraw函數(shù)中
有一個 CDC指針類型的變量 : pDC,因此直接把這個變量作為參數(shù)傳遞給 Draw函數(shù)就可
以了。
運行 Graphic程序,先選擇相關(guān)繪圖菜單,在窗口中繪制一些圖形。然后選擇【文件\保存】菜單項,
把圖形對象保存到一個指定的文件中,例如 Graphic.txt。然后關(guān)閉程序,再次運行,然后選擇【文
件\打開】菜單項,并選擇 Graphic.txt文件打開。這時就可以從該文件中加載數(shù)據(jù),并在內(nèi)存中重
新構(gòu)造 CGraph對象,隨即就可以看到在程序窗口中顯示出先前繪制的圖形了。以上就是利用 C
Archive類和可串行化類的 Serialize函數(shù)保存對象和加載對象的實現(xiàn)。
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -