數據讀寫是應用程序中必不可少的一部分,Visual C++中數據的讀寫當然也十分重要,因此VisualC++在MFC中對數據的讀寫創造了十分好的支持,這使得我們可以十分方便的實現我們對數據讀寫操作的需要。
MFC 為數據讀寫設計了三個基本的類--CFile(文件類)、CStdioFile(標准I/O文件類)、CArchive(文檔類)。其中標准I/O文件類提供相當於C的流式文件的功能,可以用文本或者二進制方式打開,可以被緩沖。文件類提供了非緩沖的二進制輸入輸出文件,它既可以與文檔類結合實現VisualC++設計中常用的文件序列化,也可以由設計者自己訂制存儲方案,實現數據的讀寫操作(此方法的兼容問題需要解決,保密性強)。文檔類是VisualC++程序設計中最常用的文件處理的方法,文檔類可以方便的實現VisualC++中大多數數據類型的讀寫操作。
文檔類不僅可以實現簡單數據結構的讀寫操作,還可以通過對CObIEct類的派生實現對復雜數據結構的讀寫操作,由於該方法是VisualC++程序設計的基本方法,本文就以一個簡單的例子來介紹可序列化類的實現方法。
實現條件:
實現序列化的的類需要滿足一系列條件:
1. 該類需要從CObject類派生(可以是間接派生);
2. 在類中中進行DECLARE_SERIAL宏定義;
3. 類存在有缺省的構造&&函數;
4. 類中實現了Serialize()&&函數,並且在其中調用基類的序列化&&函數;
5. 使用IMPLEMENT_SERIAL宏指明類名及版本號;
下面是我在學習Windows程序設計課程是實現的一個程序的一個類的部分代碼,為了方便,刪去了與本文無關的&&函數及部分語句並添加了一點注解。
class CMapVertex : public CObject//實現序列化的類一般由CObject派生
{
DECLARE_SERIAL(CMapVertex)//序列化一般會需要進行DECLARE_SERIAL宏定義
public:
CMapVertex();//實現序列化需要有缺省的構造&&函數
void Serialize(CArchive &ar);
CMap<unsigned,unsigned&,float,float&> m_pre;
//其它數據及&&函數的聲明
CMapVertex* next;
};
IMPLEMENT_SERIAL(CMapVertex,CObject,0)//序列化類一般需要指明類名及版本號
//其它&&函數的定義
void CMapVertex::Serialize(CArchive &ar)
{
if(ar.IsStoring())
{
//其它數據寫操作
}
else
{
//其它數據讀操作
}
m_pre.Serialize(ar);// MFC已經為集合類實現了序列化可以調用序列化&&函數
CObject:: Serialize(ar);
//實現序列化的類一般需要在序列化&&函數中調用其基類的序列化&&函數
}//////////////////////////////////////////////////////////////////////
class CMyMap : public CObject
{
DECLARE_SERIAL(CMyMap)
public:
CMapVertex* m_TopVertex;
UINT m_VertexNum;//記錄鏈表中的元素數目
CMyMap();
void Serialize(CArchive&ar);
//其它數據及&&函數的聲明
};
IMPLEMENT_SERIAL(CMyMap,CObject,0)
//其它&&函數的定義
void CMyMap::Serialize(CArchive &ar)
{
CMapVertex*temp=m_TopVertex;
if(ar.IsStoring())
{
ar<<m_VertexNum;//讀取時需要先知道元素的個數,所以先保存m_VertexNum
for(UINT i=0;i<m_VertexNum;i++)
{
temp->Serialize(ar);
temp=temp->next;
}
//其它數據寫操作
}
else
{
ar>>m_VertexNum;
if(m_VertexNum!=0)
{
temp=m_TopVertex=new CMapVertex;
//鏈表中的元素需要在堆中進行分配內存空間,析構時會釋放空間
m_TopVertex->Serialize(ar);
}
for(UINT i=1;i<m_VertexNum;i++)
{
temp->next=new CMapVertex;
temp=temp->next;//用缺省&&函數構造的節點的next為NULL
temp->Serialize(ar);//CMapVertex類已實現序列化
}
//其它數據讀操作
}
CObject:: Serialize(ar);
}
實現細節:
本文的第二個例子模擬了MFC中鏈表集合類的序列化方法,在MFC的集合類中就是用類似的方法完成的。我們可以通過該例看出在對象的個數不固定的時候的方法,我們需要存放對象時先存放對象的數目,在讀取的時候就可以按照存放的數目讀取固定的對象。另外我們可以發現的一個十分有趣的事情時,我們上面所說的五項內容似乎與序列化的關系不大--只要獲得文檔類對象就可以完成基本的操作。為什麼需要進行那麼多項與主題"無關"的操作呢?其實如果我們如果僅需要完成簡單的數據讀寫操作,我們的確沒有必要做那些工作,但是通過那些工作我們可以較為簡單的實現一些復雜的數據操作,比如在CObject類中已經實現了AssertValid,GetRuntimeClass,IsKindOf等類的判斷操作,而且使用析取和插入運算符也必須完整地完成上述內容,更重要的是如果我們完成了那些操作就有可能在基於模板的集合類中運用自定義的類。
集合類是在MFC編程中實現較為復雜數據結構的方法之一,MFC提供了CMap、CArray、CList三種基於模板的集合類。集合類已經實現了數據的添加、刪除、遍歷以及序列化等操作,但是基於模板的集合類並不是可以引用所有的數據類型,如果我們想使用自定義的類作為集合類的節點類型的時候,我們就需要在完成自定類的時候完成規定的一系列操作,不過單單完成要求的5點內容並不能完成設計的要求,我們還需要完成對拷貝構造&&函數、Operatror = &&函數的重載。在上面的類中我們就需要在定義中添加以下&&函數。
CMapVertex(CMapVertex& other);
CMapVertex& Operator =(CMapVertex& other);
這樣修改以後CMapVertex類就可以在模板類中使用(比如定義CList<CMapVertex, CMapVertex>),建立更加復雜的對象,而且可以使用模板類的序列化&&函數直接進行讀寫操作。
注:實踐發現CMapVertex類中不可以使用CStirng類作為成員變量,如果使用在數據讀寫的時候會出現錯誤;而且在重載拷貝構造&&函數、Operatror = &&函數時需要把關鍵成員全部包括,否則添加到鏈表或數組裡的對象會有部分成員未被賦值。
使用方法:
經過序列化以後我們就可以在程序中建立該類的對象,在文檔類的序列化&&函數可以獲得CArchive對象實現自定義數據的讀寫操作。另外需要說明的是,CArchive類對析取和插入運算符的重載只支持下表中的數據 類型
WORD DWord BYTE
int LONG Float
double CString CObject*
POINT and CPoint SIZE and CSize CTime and CtimeSpan
RECT and CRect COleCurrency ColeVariant
COleDateTime COleDateTimeSpan
如果我們需要對其他類型(如bool型)作就需要進行顯式的強制類型轉化其他類型(如BYTE)進行寫操作並通過臨時變量讀操作。自定義類型既可以使用&&keyword=%D6%B8%D5%EB&Submit=+%CB%D1%CB%F7+">指針(作為參數使用析取和插入運算符操作)進行讀寫,也可以使用Serialize&&函數進行讀寫,二者如何區分使用呢?MSDN給我們的答案是,當操作對象使靜態對象或則已經分配好內存&&keyword=%D6%B8%D5%EB&Submit=+%CB%D1%CB%F7+">指針的時候,使用Serialize&&函數;當操作對象是&&keyWord=%D6%B8%D5%EB&Submit=+%CB%D1%CB%F7+">指針且沒有動態分派內存時使用重載操作符。
注:Visual C++中可以使用bool和BOOL兩種布爾變量但是它們的機制可能不完全相同,因為聲明為BOOL型的時候可以用操作符進行讀寫操作,筆者認為BOOL與BYTE相同。