程序代码
- #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中添加了一个>>运算符的重载,但没有<<运算符的重载。但是在CArchive类中有重载<<的输出CObject的友元函数,通过它完成了类的串行化。
- _AFX_INLINE CArchive& AFXAPI operator<<(CArchive& ar, const CObject* pOb)
- { ar.WriteObject(pOb); return ar; }
该函数通过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,则写入空对象标记;如果在CArchive的m_pStoreMap中发现对象指针的映射,则写入指针的映射值;如果是第一次写入,则先写入类的CRuntimeClass信息,然后调用类的Serialize()函数来让对象自己串行化其成员变量。
CArchive::m_pStoreMap中保存了已写入对象指针和索引值的映射,写入的对象指针可以包括支持序列化的类对象指针和CRuntimeClass指针。当查到已写入的指针时,CArchive只是简单的写入索引值,后面会详细说明索引值的写入规则。
对于第一次写入对象的情况,首先要通过CArchive::WriteClass()写入对应类的CRuntimeClass结构,用于标识一个类。
- 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++;
- }
- }
函数中后面的if语句写入了类的CRuntimeClass信息。如果不是第一次写入,则写入对应类标记。否则,先写一个新类标记,再写入版本号、类名称长度、类名称字符串,接下来映射结构体指针。
- void CRuntimeClass::Store(CArchive& ar) const
-
- {
- WORD nLen = (WORD)lstrlenA(m_lpszClassName);
- ar << (WORD)m_wSchema << nLen;
- ar.Write(m_lpszClassName, nLen*sizeof(char));
- }
关于标记。
-
- #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是空对象标记
wNewClassTag表明一个新的类的开始
wClassTag通过和类标识(在m_pStoreMap中是一个CRuntimeClass指针的映射)相与,来表明接下来是类的CRuntimeClass信息。
dwBigClassTag和wClassTag作用,唯一区别在于二者写入数据不同。
wBigObjectTag用来检测映射值是否应该被改写。
nMaxMapCount是m_pStoreMap中最多能存储的指针映射对数。
写入的标记分两类:类标记和对象标记,类标表明之前已经写过了类的CRuntimeClass信息,而对象标记则表明之前已经写过了一个相同的对象。类标记在最高位是1,这也就是为什么要用wClassTag或dwBigClassTag和指针映射值进行或的原因。因为读取的时候要区分一个类是类标记或者是对象标记,则用wBigObjectTag来区分,当映射值小于wBigObjectTag时,直接写入映射值;当大于或等于映射值时,先写入wBigObjectTag来表明接下来的4个字节是一个映射值整体。