DECLARE_SERIAL的步骤

很神奇吧, ar是怎么根据文件(强调一下,是根据文件,而不是硬编码)判断需要创建什么类的.

它大概有这么几个步骤:

1. 因为DECLARE_SERIAL重载了>>操作符,所以可以保证是调用CMessg类的>>函数.

2. >>函数实际上调用的是ar的ReadObject(CRuntimeClass*)函数

3. ReadObject首先从文件中读取类判断信息(可能是一个字符串,可能是一个类索引),得到Class对应的ClassName;

4. 程序的模块状态中有所有的RuntimeClass的列表,因此,查找对应的程序支持的RuntimeClass(对比ClassName),获得对应的RuntimeClass;

5. RuntimeClass中含有创建对象的方法CreateObject,调用它,创建对应的对象.这里,因为CreateObject实际就是 New 一个对象,类似 new CMessg; 所以,为了支持序列化,必须有没有参数的构造函数.

6. 创建对象之后,调用Seralize(ar),读入真正的对象的信息.

7. 将对象的指针返回.

8. pMessg就指向一个对应的对象了. MFC 六大关键技术之仿真

DECLARE_SERIAL / IMPLEMENT_SERIAL 宏  要将<< 和>> 两个运算子多载化,还要让Serialize 函数神不知鬼不觉地放入类别声明

之中,最好的作法仍然是使用宏。

类别之能够进行文件读写动作,前提是拥有动态生成的能力,所以,MFC 设计了两个宏

DECLARE_SERIAL 和IMPLEMENT_SERIAL:

#define DECLARE_SERIAL(class_name) \

DECLARE_DYNCREATE(class_name) \

friend CArchive& AFXAPI operator>>(CArchive& ar, class_name* &pOb);

#define IMPLEMENT_SERIAL(class_name, base_class_name, wSchema) \

CObject* PASCAL class_name::CreateObject() \

{ return new class_name; } \

_IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, wSchema, \

class_name::CreateObject) \

CArchive& AFXAPI operator>>(CArchive& ar, class_name* &pOb) \

{ pOb = (class_name*) ar.ReadObject(RUNTIME_CLASS(class_name)); \

return ar; } \

为了在每一个对象被处理(读或写)之前,能够处理琐屑的工作,诸如判断是否第一次

出现、记录版本号码、记录文件名等工作,CRuntimeClass 需要两个函数Load 和Store

struct CRuntimeClass

{

// Attributes

LPCSTR m_lpszClassName;

int m_nObjectSize;

UINT m_wSchema; // schema number of the loaded class

CObject* (PASCAL* m_pfnCreateObject)(); // NULL => abstract class

CRuntimeClass* m_pBaseClass;

CObject* CreateObject();

void Store(CArchive& ar) const;

static CRuntimeClass* PASCAL Load(CArchive& ar, UINT* pwSchemaNum);

// CRuntimeClass objects linked together in simple list

static CRuntimeClass* pFirstClass; // start of class list

CRuntimeClass* m_pNextClass; // linked list of registered classes

};

你已经在上一节看过Load 函数,当时为了简化,我把它的参数拿掉,改为由屏幕上获

得类别名称,事实上它应该是从文件中读一个类别名称。至于Store 函数,是把类别名

称写入文件中:

// Runtime class serialization code

CRuntimeClass* PASCAL CRuntimeClass::Load(CArchive& ar, UINT* pwSchemaNum)

{

WORD nLen;

char szClassName[64];

CRuntimeClass* pClass;

ar >> (WORD&)(*pwSchemaNum) >> nLen;

if (nLen >= sizeof(szClassName) || ar.Read(szClassName, nLen) != nLen)

return NULL;

szClassName[nLen] = ~\0~;

for (pClass = pFirstClass; pClass != NULL; pClass = pClass->m_pNextClass)

{

if (lstrcmp(szClassName, pClass->m_lpszClassName) == 0)

return pClass;

}

return NULL; // not found

}

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));

}

class CScribDoc : public CDocument

{

DECLARE_DYNCREATE(CScribDoc)

...

};

class CStroke : public CObject

{

DECLARE_SERIAL(CStroke)

public:

void Serialize(CArchive&);

...

};

class CRectangle : public CObject

{

DECLARE_SERIAL(CRectangle)

public:

void Serialize(CArchive&);

...

};

class CCircle : public CObject

{

DECLARE_SERIAL(CCircle)

public:

void Serialize(CArchive&);

...

};

以及在.CPP 档中做这样的动作:

IMPLEMENT_DYNCREATE(CScribDoc, CDocument)

IMPLEMENT_SERIAL(CStroke, CObject, 2)

IMPLEMENT_SERIAL(CRectangle, CObject, 1)

IMPLEMENT_SERIAL(CCircle, CObject, 1)

然后呢?分头设计CStroke、CRectangle 和CCircle 的Serialize 函数吧。

当然,毫不令人意外地,MFC 源代码中的CObList 和CDWordArray 有这样的内容:

// in header files

class CDWordArray : public CObject

{

DECLARE_SERIAL(CDWordArray)

public:

void Serialize(CArchive&);

...

};

class CObList : public CObject

{

DECLARE_SERIAL(CObList)

public:

void Serialize(CArchive&);

...

};

// in implementation files

IMPLEMENT_SERIAL(CObList, CObject, 0)

IMPLEMENT_SERIAL(CDWordArray, CObject, 0)

而CObject 也多了一个虚拟函数Serialize:

class CObject

{

public:

virtual void Serialize(CArchive& ar);

...

}

DECLARE_SERIAL的步骤