程序调试--CObject对象的串行化

程序代码

#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; // make sure m_pStoreMap is initialized MapObject(NULL); if (pOb == NULL) { // save out null tag to represent NULL pointer *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); // 写入类的CRuntimeClass信息 CheckCount(); // enter in stored object table, checking for overflow (*m_pStoreMap)[(void*)pOb] = (void*)(DWORD_PTR)m_nMapCount++; // 映射对象指针 // cause the object to serialize itself ((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) { // 对于void返回值的函数,如果入参不满足或特定条件不满足,可抛出异常来处理。 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(); } // make sure m_pStoreMap is initialized MapObject(NULL); // write out class id of pOb, with high bit set to indicate new object follows DWORD nClassIndex; if ((nClassIndex = (DWORD)(DWORD_PTR)(*m_pStoreMap)[(void*)pClassRef]) != 0) { // previously seen class, write out the index tagged by high bit if (nClassIndex < wBigObjectTag) *this << (WORD)(wClassTag | nClassIndex); else { *this << wBigObjectTag; *this << (dwBigClassTag | nClassIndex); } } else { // store new class *this << wNewClassTag; // 新类标记 pClassRef->Store(*this); // 写入版本号、类名称长度、类名称字符串 CheckCount(); // store new class reference in map, checking for overflow (*m_pStoreMap)[(void*)pClassRef] = (void*)(DWORD_PTR)m_nMapCount++; // 映射对象指针 } } 

函数中后面的if语句写入了类的CRuntimeClass信息。如果不是第一次写入,则写入对应类标记。否则,先写一个新类标记,再写入版本号、类名称长度、类名称字符串,接下来映射结构体指针。

void CRuntimeClass::Store(CArchive& ar) const // stores a runtime class description { WORD nLen = (WORD)lstrlenA(m_lpszClassName); ar << (WORD)m_wSchema << nLen; ar.Write(m_lpszClassName, nLen*sizeof(char)); } 

 

关于标记。

// Pointer mapping constants #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个字节是一个映射值整体。

你可能感兴趣的:(object,File,table,null,Class,reference)