分享

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

 戴维图书馆 2013-01-23

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

分类: 深入MFC 168人阅读 评论(0) 收藏 举报

程序代码

  1. #include <afxwin.h>   
  2. class CMyClass : public CObject  
  3. {  
  4.     DECLARE_SERIAL(CMyClass)  
  5. public:  
  6.     CMyClass(int n = 10) : m_nData(n) {}  
  7.     virtual void Serialize(CArchive& ar);  
  8. protected:  
  9.     int m_nData;  
  10. };  
  11. IMPLEMENT_SERIAL(CMyClass, CObject, 1)  
  12. void CMyClass::Serialize(CArchive& ar)  
  13. {  
  14.     CObject::Serialize(ar);  
  15.     if (ar.IsStoring())  
  16.         ar<<m_nData;  
  17.     else  
  18.         ar>>m_nData;  
  19. }  
  20. int _tmain(int argc, _TCHAR* argv[])  
  21. {  
  22.     CFile file("file.dat", CFile::modeReadWrite | CFile::modeCreate);  
  23.     CArchive ar(&file, CArchive::store);  
  24.       
  25.     CMyClass* pMyClass1 = new CMyClass(2000);  
  26.     ar<<pMyClass1;  
  27.     CMyClass* pMyClass2 = new CMyClass(3000);  
  28.     ar<<pMyClass2;  
  29.     return 0;  
  30. }  
 

 


在序列化宏IMPLEMENT_SERIAL中添加了一个>>运算符的重载,但没有<<运算符的重载。但是在CArchive类中有重载<<的输出CObject的友元函数,通过它完成了类的串行化。

  1. _AFX_INLINE CArchive& AFXAPI operator<<(CArchive& ar, const CObject* pOb)  
  2.     { ar.WriteObject(pOb); return ar; }  
 

该函数通过CArchive::WriteObject()将类写入到文件中。

  1. void CArchive::WriteObject(const CObject* pOb)  
  2. {  
  3.     if (!IsStoring())   
  4.     {  
  5.         AfxThrowArchiveException(CArchiveException::readOnly, m_strFileName);  
  6.     }  
  7.     DWORD nObIndex;  
  8.     // make sure m_pStoreMap is initialized   
  9.     MapObject(NULL);  
  10.     if (pOb == NULL)  
  11.     {  
  12.         // save out null tag to represent NULL pointer   
  13.         *this << wNullTag;  
  14.     }  
  15.     else if ((nObIndex = (DWORD)(DWORD_PTR)(*m_pStoreMap)[(void*)pOb]) != 0)  
  16.     {  
  17.         // 已经串行化过的指针,再次串行化只写入指针索引值。   
  18.         if (nObIndex < wBigObjectTag)  
  19.             *this << (WORD)nObIndex;  
  20.         else  
  21.         {  
  22.             *this << wBigObjectTag;     
  23.             *this << nObIndex;  
  24.         }  
  25.     }  
  26.     else  
  27.     {  
  28.         // 第一次写入类对象   
  29.         CRuntimeClass* pClassRef = pOb->GetRuntimeClass();  
  30.         WriteClass(pClassRef);      // 写入类的CRuntimeClass信息   
  31.         CheckCount();       // enter in stored object table, checking for overflow   
  32.         (*m_pStoreMap)[(void*)pOb] = (void*)(DWORD_PTR)m_nMapCount++;   // 映射对象指针   
  33.         // cause the object to serialize itself   
  34.         ((CObject*)pOb)->Serialize(*this);       // 对象自身串行化其内部成员变量。   
  35.     }  
  36. }  
 

如果pObj为NULL,则写入空对象标记;如果在CArchive的m_pStoreMap中发现对象指针的映射,则写入指针的映射值;如果是第一次写入,则先写入类的CRuntimeClass信息,然后调用类的Serialize()函数来让对象自己串行化其成员变量。

CArchive::m_pStoreMap中保存了已写入对象指针和索引值的映射,写入的对象指针可以包括支持序列化的类对象指针和CRuntimeClass指针。当查到已写入的指针时,CArchive只是简单的写入索引值,后面会详细说明索引值的写入规则。


对于第一次写入对象的情况,首先要通过CArchive::WriteClass()写入对应类的CRuntimeClass结构,用于标识一个类。

  1. void CArchive::WriteClass(const CRuntimeClass* pClassRef)  
  2. {  
  3.     if (pClassRef == NULL)   
  4.     {       // 对于void返回值的函数,如果入参不满足或特定条件不满足,可抛出异常来处理。   
  5.         AfxThrowArchiveException(CArchiveException::badClass, m_strFileName);  
  6.     }  
  7.     if (!IsStoring())   
  8.     {  
  9.         AfxThrowArchiveException(CArchiveException::genericException, m_strFileName);  
  10.     }  
  11.     if (pClassRef->m_wSchema == 0xFFFF)  
  12.     {  
  13.         TRACE(traceAppMsg, 0, "Warning: Cannot call WriteClass/WriteObject for %hs./n",  
  14.             pClassRef->m_lpszClassName);  
  15.         AfxThrowNotSupportedException();  
  16.     }  
  17.     // make sure m_pStoreMap is initialized   
  18.     MapObject(NULL);  
  19.     // write out class id of pOb, with high bit set to indicate new object follows   
  20.     DWORD nClassIndex;  
  21.     if ((nClassIndex = (DWORD)(DWORD_PTR)(*m_pStoreMap)[(void*)pClassRef]) != 0)  
  22.     {  
  23.         // previously seen class, write out the index tagged by high bit   
  24.         if (nClassIndex < wBigObjectTag)  
  25.             *this << (WORD)(wClassTag | nClassIndex);  
  26.         else  
  27.         {  
  28.             *this << wBigObjectTag;  
  29.             *this << (dwBigClassTag | nClassIndex);  
  30.         }  
  31.     }  
  32.     else  
  33.     {  
  34.         // store new class   
  35.         *this << wNewClassTag;        // 新类标记   
  36.         pClassRef->Store(*this); // 写入版本号、类名称长度、类名称字符串   
  37.           
  38.         CheckCount();   // store new class reference in map, checking for overflow   
  39.         (*m_pStoreMap)[(void*)pClassRef] = (void*)(DWORD_PTR)m_nMapCount++;     // 映射对象指针   
  40.     }  
  41. }  
 

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

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

 


关于标记。

  1. // Pointer mapping constants   
  2. #define wNullTag        ((WORD)0)           // special tag indicating NULL ptrs   
  3. #define wNewClassTag    ((WORD)0xFFFF)      // special tag indicating new CRuntimeClass   
  4. #define wClassTag       ((WORD)0x8000)      // 0x8000 indicates class tag (OR'd)   
  5. #define dwBigClassTag   ((DWORD)0x80000000) // 0x8000000 indicates big class tag (OR'd)   
  6. #define wBigObjectTag   ((WORD)0x7FFF)      // 0x7FFF indicates DWORD object tag   
  7. #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个字节是一个映射值整体。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多