编写可序列化类的步骤
1、 直接或间接从CObject派生。
2、 在类声明中加入DECLARE_SERIAL宏。
3、 重载基本类的Serialize函数,并串行化派生类的数据成员。
4、 如果派生类没有默认的构造函数,则添加一个。因为对象加载时,MFC要用默认构造函数在浮动标签上创建对象,并用从档案取回的值设置对象数据成员的初始值。
5、 在类的实现中写入IMPLEMENT_SERIAL宏。IMPLEMENT_SERIAL宏接收三个参数:类名、父类名和版本号。
一个示例:
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)
宏展开
class CMyClass : public CObject { public: static const CRuntimeClass classCMyClass; virtual CRuntimeClass* GetRuntimeClass() const; static CObject* PASCAL CreateObject(); friend CArchive& AFXAPI operator>>(CArchive& ar, CMyClass* &pOb); public: CMyClass(int n = 10) : m_nCnt(n) { } virtual void Serialize(CArchive& ar); private: int m_nCnt; }; extern AFX_CLASSINIT _init_CMyClass; const CRuntimeClass CMyClass::classCMyClass = { "CMyClass", sizeof(class CMyClass), 1, CMyClass::CreateObject, RUNTIME_CLASS(CObject), NULL, _init_CMyClass }; CObject* PASCAL CMyClass::CreateObject() { return new CMyClass; } CRuntimeClass* CMyClass::GetRuntimeClass() const / { return RUNTIME_CLASS(CMyClass); } CArchive& AFXAPI operator>>(CArchive& ar, CMyClass* &pOb) { pOb = (CMyClass*) ar.ReadObject(RUNTIME_CLASS(CMyClass)); return ar; }
CRuntimeClass链表的形成过程。
从上面宏展开可以看出,序列化比动态创建多了一个>>运算符重载,多填写了CRuntimeClass的m_wSchema、m_pClassInit字段。从CRuntimeClass的m_pNextClass字段可以看出,如果要将所有类的CRuntimeClass组成类链表,这个字段也必须填写,但在初始化类的CRuntimeClass中并没有填写这个字段,而是将它置为NULL。
在序列化中,多了一个变量_init_CMyClass,也就是它将类的CRuntimeClass指针加入到了全局链表中。
// AFX_CLASSINIT结构体 struct AFX_CLASSINIT { AFX_CLASSINIT(CRuntimeClass* pNewClass) { AfxClassInit(pNewClass); } }; void AFXAPI AfxClassInit(CRuntimeClass* pNewClass) { AFX_MODULE_STATE* pModuleState = AfxGetModuleState(); AfxLockGlobals(CRIT_RUNTIMECLASSLIST); pModuleState->m_classList.AddHead(pNewClass); AfxUnlockGlobals(CRIT_RUNTIMECLASSLIST); }
AFX_CLASSINIT是个结构体,它没有成员变量,只有一个包含CRuntimeClass指针参数的构造函数。这个构造函数调用了AfxClassInit()函数,将类的CRuntimeClass初始化到了进程类链表中。
AfxClassInit()函数首先获得进程模块状态结构Afx_MODULE_STATE,该结构体中的m_classList成员变量即为进程类链表。通过调用m_classList.AddHead(pNewClass),类的CRuntimeClass结构被链接了起来。该链表类仅包含支持序列化的类。
m_classList是CTypedSimpleList<CRuntimeClass*>类型。调用CTypedSimpleList的成员函数AddHead(),将新的CRuntimeClass指针加入了类链表中。
void AddHead(TYPE p) { CSimpleList::AddHead(p); }
CTypedSimpleList调用了父类的CSimpleList::AddHead()。
void CSimpleList::AddHead(void* p) { *GetNextPtr(p) = m_pHead; m_pHead = p; }
可以看出一个最重要的函数CSimpleList::GetNextPtr(),该函数用来获得指向下一个元素的元素指针。也就是说,CSimpleList存储的结构体元素其中包含一个字段,用于指向链表中下一个元素。
void** CSimpleList::GetNextPtr(void* p) const { return (void**)((BYTE*)p+m_nNextOffset); }
m_nNextOffset是CSimpleList的成员变量,它记录了所存储的结构体中,指向下一个元素的元素指针在结构体中的偏移。它是如何被初始化的呢?m_classList是AFX_MODULE_STATE的成员变量。在AFX_MODULE_STATE的构造函数中,有下面这条语句:
AFX_MODULE_STATE::AFX_MODULE_STATE(BOOL bDLL) { m_classList.Construct(offsetof(CRuntimeClass, m_pNextClass)); // ... } // offsetof是个宏,用于计算结构体中字段偏移量,它的声明如下: #define offsetof(s,m) (size_t)&(((s *)0)->m)
finished...