簡介
在這個框架中包含了一個序列化的基本框架,一套基本的類型識別系統,可以識別基礎類型,復雜類型,自定義類型,STD的容器類型,而且可以這個基礎上進行遞歸的擴展。
可以將復雜的數據結構序列化到文件,并從文件中恢復。
包含了完整的自動單元測試,和測試案例,點此下載。
正文
寫這個序列化框架最初是想用在一個大型的項目上,在那個項目中有一些相當復雜的在運行時構建出來的樹形數據結構,如果可以將這個內存樹序列化起來可以大大節約下次創建的時間。另外在自己做的一些小工具中,有些數據想保存在文件中,以后再從文件中讀取,用序列化的方式也十分方便。而且那時正好系統的學習了一下C++模板技術,感覺在一般的編程活動中很難用到一些比較高級的模板技術,所以想用C++模板技術來寫這個序列化框架。最后那個項目中沒有使用這個序列化框架,但我至少達到了第二個目標,寫這個序列化框架讓我對C++模板技術有了更深層次的理解。
在這個框架中包含了一個序列化的基本框架,一套基本的類型識別系統,可以識別基礎類型,復雜類型,自定義類型,STD的容器類型,而且可以這個基礎上進行遞歸的擴展。
在寫這個框架的同時,我也寫了完整的測試案例。如果沒有測試案例,要調試這樣的框架可就真是難與登天,因為模板方面的錯誤,編譯器報出來的信息很難看,有的根本就沒用。
代碼是在VC7.1下寫的,也只能在VC7.1下用,VC6對于C++模板的支持非常有限,而其他的編譯器在這方面的支持也有出入。如果要用于其他的編譯器可能要修改部分類型識別方面的代碼。測試框架我用的是cppunit(1.9.14),這是個開源的測試框架,可以在www.xprogramming.com下載到。其中類型識別方面的代碼我主要是參考了《C++ template》一書,和boost中的部分代碼。
由于是用模板寫的比MFC中的運行時序列化框架在效率上的表現要好得多。使用起來也相當的簡單。如果要學習C++模板的高級技術,研究一下這個框架可以獲益良多。由于是框架代碼,我寫得相當規范,有注釋,也有完整的測試案例,可以進行自動的回歸測試。
使用的方法比較簡單請參考(fileRWTest.cpp)文件中的測試案例。
普通的數據類型:
(unsigned char, unsigned short, unsigned int, unsigned long, signed char, signed short, signed int, signed long, bool, char, wchar_t, unsigned long long, signed long long, float, double, long double)可以直接序列化及反序列化。
對于指針類型:
會序列化指針具體指向的對象,如果指針指向的對象的類型是序列化框架無法識別的類型會報出編譯錯誤。注意在反序列化時,只需要傳一個空指針即可,序列化框架會將被序列化的對象的值反序列化到堆上,并將地址付給指針。如果傳一個有值的指針,在DEBUG模式下會在運行時引發一個斷言錯誤。在 RELEASE下會導致原來指針指向的對象被泄漏。
對于普通數據類型的數組:
會將整個數組以內存拷貝的方式序列化到內存,即使沒被真正賦值的元素。反序列化時傳一個相同類型的數組即可。需要注意的是,傳進的數組的容量必須大于或等于被序列化的數組的容量,否則會引發數組越界的內存錯誤,在DEBUG模式下,會引發一個斷言錯誤。
非普通數據類型的數組:
數組元素的類型可以是除普通數據類型之外的所有被序列化框架所支持的類型。序列化時會針對每一個元素調用序列化框架對它的具體序列化特化,反序列化時亦然。由于在RELEASE模式下類類型的數組在申明后,編譯器會生成調用相應類的缺省構造函數的代碼。但對于原始類型,如指針數組類型如果不顯式的手工初始化,數組中的值是無意的隨機值。這種情況序列化框架無法識別,會贊成嚴重的內存錯誤。另對于指針數組的某些元素為NULL的情況,序列化框架也無法處理,在DEBUG模式下會引發一個斷言錯誤。
因些如果是指針數組除非數組中的元素全部為有意義的指針,否則不應該做為一個數組來序列化,而應該加入相應的遍歷邏輯,將有意思的元素逐個序列化。
對于一般的數組,如果有意思的只是其中的少部分元素,也應該以上述方式進行序列化,以提高性能。
自定義數據類類型:
不需要拷貝構造函數,不需要拷貝賦值函數,不需要析構函數的類。如老式的struct結構類型。這種類型可以通過直接拷貝內存而被高效的序列化及化序列化。只需要讓一個類從_data_class_tag派生,序列化框架就會將它當成普通的數據類類型處理。
自定義復雜類型:
對于非數據類類型,必須從CSerializable派生,關在類的定義中加入SERIALIZABLE(name, x)宏,name是該類的名字,x是相應的版本號。版本號的引入主要是避免在一個類被修改后,和以前生成的序列化文件一起使用,以免引起內存錯誤。在類中還必須實現virtual bool Serialize(CMedia *) const;函數,在該函數中寫具體的序列化代碼。該函數的內容很簡單,按序列化及反序列化用為兩段,簡單的為每一個需要序列化及反序列化的成員函數調用即可,如下列:
if (pMedia->IsStoring()) {
*pMedia << m_1 << m_2 << m_3 << m_4 << m_5;
return true;
}
if (pMedia->IsLoading()) {
*pMedia >> m_1 >> m_2 >> m_3 >> m_4 >> m_5;
return true;
}
注意序列化和反序列化的順序這要錯。
std::string及std::wstring類型:
使用比較簡單。值得注意的是和將字符串數組做字符指針用的情況一樣。如果申明了一個容量很大的string(一般是為了避免在追加時的內存重分配開銷),卻只用了一小部分。序列化并反序列化,string對象的容量只是剛好有內存的那部分。
std::pair類型:
只要是pair的first和second必須是序列化框架所支持的類型就可以被正常的序列化及反序列化。
std容器類型:
(vector,list,deque,stack,queue,set,multiset,map,multimap)
支持以上的容器類型,其中容器中的元素類型必須是序列化框架所支持的類型。
posted on 2008-07-19 13:15
chatler 閱讀(290)
評論(0) 編輯 收藏 引用 所屬分類:
C++_BASIS