天天看點

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()成員函數,從檔案讀取對象資料初始化新生成的對象。

  4 document/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虛函數中是好時機。代碼如下:

c++代碼

int ncount;  

ncount=m_obarray.getsize();  

for(int i=0;i<ncount;i++)  

{  

    delete m_obarray.getat(i);//釋放指針指向的記憶體空間  

   //m_obarray.removeat(i);//移除連結清單中的元素。嘿嘿,别搞錯了。但在此處不能這樣用,會導緻非法操作。要用下面的方法  

}  

array.removeall();  

  還有一種方法:

while(ncount--)  

     delete m_obarray.getat(ncount);  

     m_obarray.removeat(ncount);  

}