程序代碼
- #include <afxwin.h>
- class CMyClass : public CObject
- {
- DECLARE_SERIAL(CMyClass)
- public:
- CMyClass(int n = 10) : m_nData(n) {}
- virtual void Serialize(CArchive& ar);
- protected:
- int m_nData;
- };
- IMPLEMENT_SERIAL(CMyClass, CObject, 1)
- void CMyClass::Serialize(CArchive& ar)
- {
- CObject::Serialize(ar);
- if (ar.IsStoring())
- ar<<m_nData;
- else
- ar>>m_nData;
- }
- int _tmain(int argc, _TCHAR* argv[])
- {
- CFile file("file.dat", CFile::modeReadWrite | CFile::modeCreate);
- CArchive ar(&file, CArchive::store);
-
- CMyClass* pMyClass1 = new CMyClass(2000);
- ar<<pMyClass1;
- CMyClass* pMyClass2 = new CMyClass(3000);
- ar<<pMyClass2;
- return 0;
- }
在序列化宏IMPLEMENT_SERIAL中添加了一個(gè)>>運(yùn)算符的重載,但沒有<<運(yùn)算符的重載。但是在CArchive類中有重載<<的輸出CObject的友元函數(shù),通過它完成了類的串行化。
- _AFX_INLINE CArchive& AFXAPI operator<<(CArchive& ar, const CObject* pOb)
- { ar.WriteObject(pOb); return ar; }
該函數(shù)通過CArchive::WriteObject()將類寫入到文件中。
- void CArchive::WriteObject(const CObject* pOb)
- {
- if (!IsStoring())
- {
- AfxThrowArchiveException(CArchiveException::readOnly, m_strFileName);
- }
- DWORD nObIndex;
-
- MapObject(NULL);
- if (pOb == NULL)
- {
-
- *this << wNullTag;
- }
- else if ((nObIndex = (DWORD)(DWORD_PTR)(*m_pStoreMap)[(void*)pOb]) != 0)
- {
-
- if (nObIndex < wBigObjectTag)
- *this << (WORD)nObIndex;
- else
- {
- *this << wBigObjectTag;
- *this << nObIndex;
- }
- }
- else
- {
-
- CRuntimeClass* pClassRef = pOb->GetRuntimeClass();
- WriteClass(pClassRef);
- CheckCount();
- (*m_pStoreMap)[(void*)pOb] = (void*)(DWORD_PTR)m_nMapCount++;
-
- ((CObject*)pOb)->Serialize(*this);
- }
- }
如果pObj為NULL,則寫入空對象標(biāo)記;如果在CArchive的m_pStoreMap中發(fā)現(xiàn)對象指針的映射,則寫入指針的映射值;如果是第一次寫入,則先寫入類的CRuntimeClass信息,然后調(diào)用類的Serialize()函數(shù)來讓對象自己串行化其成員變量。
CArchive::m_pStoreMap中保存了已寫入對象指針和索引值的映射,寫入的對象指針可以包括支持序列化的類對象指針和CRuntimeClass指針。當(dāng)查到已寫入的指針時(shí),CArchive只是簡單的寫入索引值,后面會(huì)詳細(xì)說明索引值的寫入規(guī)則。
對于第一次寫入對象的情況,首先要通過CArchive::WriteClass()寫入對應(yīng)類的CRuntimeClass結(jié)構(gòu),用于標(biāo)識一個(gè)類。
- void CArchive::WriteClass(const CRuntimeClass* pClassRef)
- {
- if (pClassRef == NULL)
- {
- AfxThrowArchiveException(CArchiveException::badClass, m_strFileName);
- }
- if (!IsStoring())
- {
- AfxThrowArchiveException(CArchiveException::genericException, m_strFileName);
- }
- if (pClassRef->m_wSchema == 0xFFFF)
- {
- TRACE(traceAppMsg, 0, "Warning: Cannot call WriteClass/WriteObject for %hs./n",
- pClassRef->m_lpszClassName);
- AfxThrowNotSupportedException();
- }
-
- MapObject(NULL);
-
- DWORD nClassIndex;
- if ((nClassIndex = (DWORD)(DWORD_PTR)(*m_pStoreMap)[(void*)pClassRef]) != 0)
- {
-
- if (nClassIndex < wBigObjectTag)
- *this << (WORD)(wClassTag | nClassIndex);
- else
- {
- *this << wBigObjectTag;
- *this << (dwBigClassTag | nClassIndex);
- }
- }
- else
- {
-
- *this << wNewClassTag;
- pClassRef->Store(*this);
-
- CheckCount();
- (*m_pStoreMap)[(void*)pClassRef] = (void*)(DWORD_PTR)m_nMapCount++;
- }
- }
函數(shù)中后面的if語句寫入了類的CRuntimeClass信息。如果不是第一次寫入,則寫入對應(yīng)類標(biāo)記。否則,先寫一個(gè)新類標(biāo)記,再寫入版本號、類名稱長度、類名稱字符串,接下來映射結(jié)構(gòu)體指針。
- void CRuntimeClass::Store(CArchive& ar) const
-
- {
- WORD nLen = (WORD)lstrlenA(m_lpszClassName);
- ar << (WORD)m_wSchema << nLen;
- ar.Write(m_lpszClassName, nLen*sizeof(char));
- }
關(guān)于標(biāo)記。
-
- #define wNullTag ((WORD)0) // special tag indicating NULL ptrs
- #define wNewClassTag ((WORD)0xFFFF) // special tag indicating new CRuntimeClass
- #define wClassTag ((WORD)0x8000) // 0x8000 indicates class tag (OR'd)
- #define dwBigClassTag ((DWORD)0x80000000) // 0x8000000 indicates big class tag (OR'd)
- #define wBigObjectTag ((WORD)0x7FFF) // 0x7FFF indicates DWORD object tag
- #define nMaxMapCount ((DWORD)0x3FFFFFFE) // 0x3FFFFFFE last valid mapCount
wNullTag是空對象標(biāo)記
wNewClassTag表明一個(gè)新的類的開始
wClassTag通過和類標(biāo)識(在m_pStoreMap中是一個(gè)CRuntimeClass指針的映射)相與,來表明接下來是類的CRuntimeClass信息。
dwBigClassTag和wClassTag作用,唯一區(qū)別在于二者寫入數(shù)據(jù)不同。
wBigObjectTag用來檢測映射值是否應(yīng)該被改寫。
nMaxMapCount是m_pStoreMap中最多能存儲(chǔ)的指針映射對數(shù)。
寫入的標(biāo)記分兩類:類標(biāo)記和對象標(biāo)記,類標(biāo)表明之前已經(jīng)寫過了類的CRuntimeClass信息,而對象標(biāo)記則表明之前已經(jīng)寫過了一個(gè)相同的對象。類標(biāo)記在最高位是1,這也就是為什么要用wClassTag或dwBigClassTag和指針映射值進(jìn)行或的原因。因?yàn)樽x取的時(shí)候要區(qū)分一個(gè)類是類標(biāo)記或者是對象標(biāo)記,則用wBigObjectTag來區(qū)分,當(dāng)映射值小于wBigObjectTag時(shí),直接寫入映射值;當(dāng)大于或等于映射值時(shí),先寫入wBigObjectTag來表明接下來的4個(gè)字節(jié)是一個(gè)映射值整體。