1. 程式人生 > >深入淺出MFC:再來一發~~~MFC的Persistence(永久儲存)機制

深入淺出MFC:再來一發~~~MFC的Persistence(永久儲存)機制

這一部分:侯捷大大說要模擬的東西太多了,所以沒有了樣例工程,說實話還有點小失落呢~~~~~~

原本不準備寫這次的筆記了,不過想想還是記錄下來,至少以後留個念想~~,

(還有一件事:侯捷大大說在第8章會詳細地剖析這部分內容,所以後面我應該還是會有機會來詳細的看看這個模組的內容~~~~小驚喜。。。暫時就讓我先體會體會精神吧~~)

廢話不多說了,直接進入正題:

MFC有自己實現了一套Serialize的機制,目的即是封裝關於序列化過程中的檔名的選擇,檔案的開關,緩衝區的建立,資料的讀寫,提取運算子(>>)和插入運算子的過載(<<),物件的動態建立,當然動態建立在上一章已經全部完成了~~~

在MFC中每次序列化記錄物件內容的時候都是會先寫入一個程式碼:表示次型別的物件是否已經被記錄過了,如果是新的型別就乖乖的記錄其類名稱及基本資訊,如果是已經記錄過了的型別的物件,則直接以程式碼表示,這樣可以節省檔案大小和程式解析的時間。(可惜這裡沒有程式碼)

在MFC中,每一個可以寫入到檔案或者從檔案讀取的類都應該有它自己的Serailize函式,負責他自己的資料讀寫檔案的操作,且此類應當改寫<<和>>運算子,以負責將資料流與檔案緩衝區(archive)的互動。

對於>>和<<的倆個運算子的過載,根據MFC的尿性,還是喜歡通過使用巨集定義來實現這些(大大減少程式碼的冗餘,不過貌似可讀性也降低了一點),首先說一點,類之所以可以進行讀寫操作,前提是擁有動態操作的能力,所以關於序列化的巨集也不由自主的變成了之前動態建立巨集的再封裝~~~~(基於之前動態建立基礎的擴充套件)

樣例程式碼如下:

#define DECLARE_SERIAL(class_name) \
		DECLARE_DYNCREATE(class_name) \
		friend CArchive& AFXAPI operator>>(CArchive& ar ,class_name* &pOb);

#define IMPLEMENT_SERIAL(class_name , base_class_name , wSchema) \
		CObject* PASCAL class_name::CreateObject() \
			{	return new class_name ; } \
		_IMPLEMENT_RUNTIMECLASS(class_name , base_class_name ,wSchema , \
				class_name::CreateObject) \
		CArchive& AFXAPI operator>>(CArchive& ar ,class_name* &pOb) \
			{ pOb = (class_name*) ar.ReadObject(RUNTIME_CLASS(class_name)); \
					return ar; }

為了每一個被處理的物件被處理之前可以檢查是否已經儲存過該類物件,記錄版本號碼,記錄檔名等等,我們的型別儲存單元CRuntimeClass也需要做出一些對應的改變:

樣例程式碼如下:

struct CRuntimeClass
{
	LPCSTR m_lpszClassName;
	int m_nObjectSize;
	UINT m_wSchema;
	CObject* (PASCAL* m_pfnCreateObject)();
	CRuntimeClass* m_pBaseClass;

	CObject* CreateObject();
	void Store(CArcive& ar) const;
	statc CRuntimeClass* PASCAL load(CArchive& ar, UINT* pwSchemaNum);

	static CRuntimeClass* pFirstClass;
	CRuntimeClass* m_pNextClass;
};
其實也就是加了一個Store和Load函式(這裡的Load函式才是原貌,之前的版本由於無需考慮哪些問題,所以直接過濾了引數)

最後別忘了在每個需要序列化類的實現中加上一個宣告:

void Serialize(CArchive&)
之前說過的,每一個可以序列化的類都要有自己的序列化函式,,,

其實我想這個宣告可以加在巨集定義裡面的(反正每個序列化的類都要有這個東西),實現再單獨寫不就好了嘛,不過我猜大概是由於他們不喜歡產生這種會報錯的程式碼,所以~~~~這個函式我們只得自己來宣告~~~~