文檔數(shù)據(jù)保存到文件或從文件中讀取數(shù)據(jù),都是通過序列化函數(shù)Serializ()實現(xiàn)的。
1.函數(shù)Serialize()的原理
該函數(shù)的應(yīng)用形式如下:
void CMyTestDoc::Serialize(CArchive& ar){
if(ar.IsStoring()){
ar<<m_Name<<m_Num;
}
else{
ar>>m_Name>>_Num;
}
}
函數(shù)Serialize()的參數(shù)ar是檔案類CArchive的對象的引用,其包含一個CFile類型的文件指針。CArchive對象為讀寫
CFile對象中的可串行化數(shù)據(jù)提供了類型安全緩沖機制。CArchive對象只能讀數(shù)據(jù)或?qū)憯?shù)據(jù),而不能同時讀寫數(shù)據(jù)。當保存數(shù)據(jù)到對象ar時,該對象先將數(shù)據(jù)放至緩沖區(qū)中,直到緩沖區(qū)滿,該對象才將數(shù)據(jù)寫入文件指針指向的CFile對象中。當從對象ar讀數(shù)據(jù)時,該對象從文件中讀取內(nèi)容到緩沖區(qū),然后再從緩沖區(qū)讀入到可串行化的對象中。該安全緩沖機制先集中在緩沖區(qū)中操作,減少了訪問磁盤的次數(shù),提高了不起應(yīng)用程序的性能。
ar保存了打開文件的信息以及讀或?qū)懙刃畔ⅰ.斚蛭募袑憯?shù)據(jù)時,執(zhí)行ar.IsStoring(),操作符"<<"把數(shù)據(jù)存到ar定制的文件。當從文件讀數(shù)據(jù)時,操作符">>"從文件中讀出數(shù)據(jù),并初始化相關(guān)成員變量。
(ar是由應(yīng)用程序框架完成初始化的.)
2.函數(shù)Serialize()的調(diào)用
當讀取文件或保存文件時,都要發(fā)生文檔對象的序列化操作,即調(diào)用函數(shù)Serialize().
(1)讀取操作
<1>單擊應(yīng)用程序菜單“File|New”,應(yīng)用程序調(diào)用函數(shù)OnNewDocument()建立新文檔。OnNewDocument()調(diào)用虛函數(shù)DeleteContents()清空文檔類數(shù)據(jù)成員,然后再調(diào)用SetModifiedFlag(FALSE)將文檔修改標志清除。
<2>單擊應(yīng)用程序菜單"File|Open",應(yīng)用程序調(diào)用函數(shù)OnOpenDocument()打開已有文檔。函數(shù)OnOpenDocument()調(diào)用GetFile()獲取給定文件的CFile指針,再調(diào)用DeleteContents()函數(shù)清空文檔類數(shù)據(jù)成員,然后把CFile指針構(gòu)造CArchive對象交給Serialize函數(shù)完成讀文件重建文檔對象的工作,再調(diào)用SetModifiedFlag(FALSE)將文檔修改標志清除。其執(zhí)行流程如圖:
(2)保存操作。
單擊應(yīng)用程序菜單“File|Save”或者“File|Save As”時,應(yīng)用程序調(diào)用函數(shù)OnSaveDocument()保存指定指定文件名的文檔。函數(shù)OnSaveDocument()詢問文件名,調(diào)用函數(shù)GetFile()獲得給文件的CFile指針,再調(diào)用函數(shù)DeleteContents()清空文檔類數(shù)據(jù)成員,接著把CFile指針構(gòu)造CArchive對象交給Serialize函數(shù)完成讀文件重建文檔對象的工作,最后調(diào)用SetModifiedFlag(FALSE)將文檔修改標志清除。其執(zhí)行流程如圖:
(3)可序列化的數(shù)據(jù)類型
(4)可序列化函數(shù)的局限性
一般情況下,序列化函數(shù)可以完成文檔數(shù)據(jù)的序列化操作,但是該函數(shù)也存在一定的局限性。
<1>序列化函數(shù)Serialize()只能順序讀寫文件,而不能隨機操作。并且該函數(shù)只能一次性讀寫全部文件,而不能讀取文件的部分內(nèi)容。針對該限制,開發(fā)人員可以通過獲取CFile指針,再調(diào)用CFile::Open(),CFile::Read(),CFile::Write()等函數(shù),來支持隨機或部分讀寫文件。
<2>序列化函數(shù)Serialize只能操作二進制文件,不能處理文本文件,即利用該函數(shù)保存的文件不能像文本文件那樣個具有可讀性。類CSdioFile可產(chǎn)生可讀性文件,在應(yīng)用程序框架中可利用類CStdioFile解決二進制文件不可讀問題。
<3>序列化函數(shù)Serialize()不支持數(shù)據(jù)文件操作,也不能共享操作文件。開發(fā)人員可以通過重載虛函數(shù)OnOpenDocument(),OnNewDocument()和OnSaveDocument(), 并在這些虛函數(shù)內(nèi)按數(shù)據(jù)庫訪問接口編寫讀寫數(shù)據(jù)代碼來訪問數(shù)據(jù)庫文件,而把對數(shù)據(jù)庫的操作和并發(fā)控制等任務(wù)交給數(shù)據(jù)庫管理系統(tǒng)處理。
(4)自定義可序列化類
某類只有派生自CObject且重定義了Serialize()函數(shù)才能實現(xiàn)序列化,MFC定義了兩個宏支持序列化,它們是DECLARE_SERIAL和IMPLEMENT_SERIAL.自定義一個可序列化類的具體步驟如下:
<1>將類的基類定義為CObject或其派生類
自定義的派生于CObject的類,可以訪問CObject類的基本序列化協(xié)議和功能。其代碼如下:
class CStudent::public CObject{
public:
CStudent();
virtual ~CStudent();
};
<2>覆蓋該類的Serialize成員函數(shù)
序列化函數(shù)被定義在類CObject中,其操作序列化描述對象當前狀態(tài)所必需的數(shù)據(jù)。成員函數(shù)Serialize的參數(shù)是一個CArchive對象的引用,用來讀寫對象數(shù)據(jù)。
CArchive對象的成員函數(shù)IsStoring()用來表示序列化是存儲還是讀取。其具體代碼如下:
類CStudent
//Student.h
class CStudent:public CObject{
public:
CString m_Name;
int m_Num;
...
void Serialize(CArchive& ar);
};
//Student.cpp
void CStudent::Serialize(CArchive& ar){
//調(diào)用基類的Serialize()確保對象的繼承部分被序列化
CObject::Serialize(ar);
if(ar.IsStoring())
ar<<m_Name<<m_Num;
else
ar>>m_Name>>m_Num;
<3>在類聲明中,添加DECLARE_SERIAL宏
在類的聲明中,添加DECARE_SERIAL宏,以類名為唯一參數(shù),其代碼如下:
class CStudent:public CObject{
DECLARE_SERIAL(CStudent)
...
}
<4>定義不帶參數(shù)的構(gòu)造函數(shù)
在類的聲明中定義一個默認構(gòu)造函數(shù)(不帶任何參數(shù)的構(gòu)造函數(shù))。一般情況下,添加類時會自動生成。當從數(shù)據(jù)文件讀入數(shù)據(jù)時,應(yīng)用程序框架需要一個默認構(gòu)造函數(shù)對象。
(如果開發(fā)人員沒有在使用宏的類中定義默認的構(gòu)造函數(shù),編譯器會在宏IMPLEMENT_SERIAL所在的行警告沒有定義默認構(gòu)造函數(shù)。)
<5>在類的實現(xiàn)文件中,添加IMPLEMENT_SERIAL宏
在類的實現(xiàn)文件(.cpp文件)中,添加IMPLEMENT_SERIAL宏,它需要3個參數(shù),分別是需要序列化的類名。其基類名及版本號。用于類的數(shù)據(jù)成員或文檔數(shù)據(jù)在程序不同版本中可能不同,那么序列化內(nèi)容也不同。如果保存的數(shù)據(jù)與讀取對象的版本數(shù)據(jù)不同,序列化操作會出現(xiàn)嚴重錯誤。因此,應(yīng)用程序應(yīng)該使用版本號,它代表了數(shù)據(jù)的組成和類的結(jié)構(gòu)?;鶎崿F(xiàn)代碼如下:
IMPLEMENT_SERIAL(CStudent,CObject,1) //讓此類有序列化能力
本站僅提供存儲服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請
點擊舉報。