5.3.3 序列化
视频精讲:光盘\video\05\序列化.swf
序列化也需要得到CRuntimeClass结构的支持,它指的是从一个持久性存储介质中读出或者写入一个对象的过程。在MFC中,为了实现序列化,可以执行以下几步。
(1)令需要添加序列化支持的类继承于CObject类或其派生类。
(2)重载CObject类的Serialize()成员函数。
(3)在类的声明中添加DECLARE_SERIAL()宏。
(4)定义一个无参构造函数。
(5)在类的实现文件中添加IMPLEMENT_SERIAL()宏。
重载Serialize()的目的是让文档对象在执行打开/保存操作时,能够读取和存储复杂的类对象,其基本形式如下:
void CMFCDoc::Serialize(CArchive& ar) { if (ar.IsStoring()) { //在此处添加存储对象的代码 } else { //在此处添加读取对象的代码 } }
|
DECLARE_SERIAL()宏的作用是提供序列化的声明。在afx.h头文件下,宏DECLARE_ SERIAL ()的定义如下:
#define DECLARE_SERIAL(class_name) \ _DECLARE_DYNCREATE(class_name) \ AFX_API friend CArchive& AFXAPI operator>> (CArchive& ar, class_name* &pOb); |
而宏IMPLEMENT_SERIAL()的作用则是提供序列化的实现,其定义如下:
#define IMPLEMENT_SERIAL(class_name, base_class_name, wSchema) \ CObject* PASCAL class_name::CreateObject() \ { return new class_name; } \ extern AFX_CLASSINIT _init_##class_name; \ _IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, wSchema, \ class_name::CreateObject, &_init_##class_name) \ AFX_CLASSINIT _init_##class_name(RUNTIME_CLASS(class_name)); \ //实现重载的">>"操作符函数以读取对象 CArchive& AFXAPI operator>>(CArchive& ar, class_name* &pOb) \ { pOb = (class_name*) ar.ReadObject(RUNTIME_CLASS(class_name)); \ return ar; }
|
上述宏定义中,CArchive类是作为待序列化的对象与存储介质之间的一种媒介而存在的,该类的对象可以被看做是一个二进制流,用于实现对象的可序列化读写。使用CArchive对象时特别值得注意的是,对象的可序列化读写不能同时进行,即对于给定的CArchive对象在同一时间内只能执行单向地存储或读取操作。
在实现序列化的过程中,DECLARE_SERIAL()宏首先通过_DECLARE_DYNCREATE()宏为类定义了动态创建,然后,再以友元的形式重载操作符">>"。IMPLEMENT_SERIAL()宏给出了该重载函数的具体实现,它通过类的CreateObject()函数及">>"操作符调用CArchive对象的ReadObject()成员函数,以根据所获得的CRuntimeClass结构的内容从存储介质中读出对象。另外,用于保存对象的"<<"运算符的实现则是通过CArchive类的WriteObject()成员函数实现的,其代码如下:
AFXINLINE CArchive& AFXAPI operator<<(CArchive& ar, const CObject* pOb) { ar.WriteObject(pOb); return ar; } |
下面来看一下CArchive类的WriteObject()及ReadObject()成员函数的关键代码:
WriteObject(): void CArchive::WriteObject(const CObject* pOb) { ……//此处代码省略 if (pOb == NULL) { ……//此处代码省略 } else if ((nObIndex = (DWORD)(DWORD_PTR)(*m_pStoreMap)[(void*)pOb]) != 0) { ……//此处代码省略 } else { //初始化对象的CRuntimeClass结构 CRuntimeClass* pClassRef = pOb->GetRuntimeClass(); //保存类信息 WriteClass(pClassRef); ……//此处代码省略 //调用对象的Serialize()成员函数实现序列化 ((CObject*)pOb)->Serialize(*this); } } } ReadObject(): CObject* CArchive::ReadObject(const CRuntimeClass* pClassRefRequested) { ……//此处代码省略 UINT nSchema; DWORD obTag; //读取对象的CRuntimeClass结构信息 CRuntimeClass* pClassRef = ReadClass (pClassRefRequested, &nSchema, &obTag); CObject* pOb=NULL; if (pClassRef == NULL) { ……//此处代码省略 } else { TRY { //动态创建对象 pOb = pClassRef->CreateObject(); ……//此处代码省略 UINT nSchemaSave = m_nObjectSchema; m_nObjectSchema = nSchema; //调用对象的Serialize()成员函数初始化对象的数据成员 pOb->Serialize(*this); m_nObjectSchema = nSchemaSave; ASSERT_VALID(pOb); } CATCH_ALL(e) { if(pOb!=NULL) { delete pOb; pOb=NULL; } THROW_LAST(); } END_CATCH_ALL } return pOb; } |
至此,我们可以看出,序列化存储对象的基本流程是:首先,写入初始化对象CRuntimeClass结构中的信息;然后,调用对象的Serialize()方法实现序列化。而序列化读取对象的基本流程则是:第一,获取对象的CRuntimeClass结构的信息;第二,根据所得的信息动态地创建对象;第三,调用对象的Serialize()方法初始化对象的数据成员。
【责任编辑:
云霞 TEL:(010)68476606】