分享
 
 
 

DECLARE_SERIAL

王朝百科·作者佚名  2012-03-05
窄屏简体版  字體: |||超大  

DECLARE_SERIAL / IMPLEMENT_SERIAL 宏的技术详解(转) DECLARE_DYNAMIC表明的是支持类型信息, 有了这个宏,我们就可以判断一个类究竟是什么类,比如

class A;

class B:public A;

A a;

B b;

现在有一个指针 class *pA 它指向一个对象, 请问你怎么知道pA指向的是a对象还是b对象,这是如果有类型信息,我们就可以知道pA到底是什么对象, 其实,它内部的实现原理是一个字符串,所以,进行这个判断时,实际上是字符串比较.

DECLARE_DYNCREATE是动态创建的意思.这个有点类似Com的类工厂.

它实际上是用类CRunTime class记录了类的静态创建函数的地址.这个特性在很多地方需要使用.就在下面说的DECLARE_SERIAL就是一个经典的例子.

动态创建主要用在 "我不知道要创建的对象就是是什么类,但是我知道它肯定是从某个基类派生的".

DECLARE_SERIAL是指序列化特性,它是一个完全自动化的存储机制,它可以将一个对象数组(可能含有A,B,C类的对象)存储进去,而且能够根据存储的情况准确的载入进来,这看起来很简单, 但是,有一个问题我们必须考虑, 就是怎么写这个程序,使得载入的时候能够正确创建相应的A,B,C类的对象呢(注意,这里是三个不同的类).而且MFC的设计人员当初编写这个机制的时候根本不知道到底会出现什么类,也许还会出现D类. 怎么办呢?

可以肯定,存储机制中必须要有能够判断类种类的代码.所以,序列化机制DECLARE_SERIAL包含了DECLARE_DYNAMIC,这样在存储进入文件的时候,可以将类名称存储到文件中.

OK,现在我们载入的时候可以知道我们要载入什么类了,但是,我们又要怎么去创建它呢? 所以DECLARE_SERIAL也包含了DECLARE_DYNCREATE,它用于创建对象.

那么,DECLARE_SERIAL到底有什么特殊的地方呢?首先,它必须实现operator>>(具体原因可以看看深入浅出,还有版本控制,这样,我们在处理序列化时,可以很灵活.

首先记住一点,DECLARE_SERIAL最主要的用途是一种智能存储.所以我们可以不用这个智能特性.

当我们没有DECLARE_SERIAL,而有void CMessg::Serialize(CArchive& ar)时,我们只能这样进行存储

CDocument::Serialize(ar)

{

if (ar.isstoring())

{

//存储一个对象

pMessg->Serialize(ar);

}

else

{

//必须非常明确的指出New一个 CMessg对象;

pMessg = new CMessg;

pMessg->Serialize(ar);

}

}

在上面这个例子中,根本没有利用MFC为我们设计的序列化只能机制.

再看下面一个例子

CDocument::Serialize(ar)

{

if (ar.isstoring())

{

//存储一个对象

ar << pMessg;

}

else

{

//必须非常明确的指出New一个 CMessg对象;

ASSERT(pMessg == NULL);

ar >> pMessg;

}

}

很神奇吧, 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] = ~~;

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

...

}

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有