else
archive>>m_name>>m_number;
}
使用运行时类信息:
CPersona;
ASSERT(a.IsKindOf(RUNTIME_CLASS(CPerson)));
ASSERT(a.IsKindOf(RUNTIME_CLASS(CObject)));
动态创建:
CRuntimeClass*pRuntimeClass=RUNTIME_CLASS(CPerson)
//Cperson有一个缺省构造函数
CObject*pObject=pRuntimeClass->CreateObject();
Assert(pObject->IsKindOf(RUNTIME_CLASS(CPerson));
1.实现CObject特性的机制
由上,清楚了CObject的结构,也清楚了从CObject派生新类时程序员使用CObject特性的方法。
现在来考察这些方法如何利用CObjet的结构,CObject结构如何支持这些方法。
首先,要揭示DECLARE_DYNAMIC等宏的内容,然后,分析这些宏的作用。
1.DECLARE_DYNAMIC等宏的定义
MFC提供了DECLARE_DYNAMIC、DECLARE_DYNCREATE、DECLARE_SERIAL声明宏的两种定义,分别用于静态链接到MFCDLL和动态链接到MFCDLL。
对应的实现宏IMPLEMNET_XXXX也有两种定义,但是,这里实现宏就不列举了。
MFC对这些宏的定义如下:
#ifdef_AFXDLL//动态链接到MFCDLL
#defineDECLARE_DYNAMIC(class_name)\
protected:
\
staticCRuntimeClass*PASCAL_GetBaseClass();\
public:
\
staticconstAFX_DATACRuntimeClassclass##class_name;\
virtualCRuntimeClass*GetRuntimeClass()const;\
#define_DECLARE_DYNAMIC(class_name)\
protected:
\
staticCRuntimeClass*PASCAL_GetBaseClass();\
public:
\
staticAFX_DATACRuntimeClassclass##class_name;\
virtualCRuntimeClass*GetRuntimeClass()const;\
#else
#defineDECLARE_DYNAMIC(class_name)\
public:
\
staticconstAFX_DATACRuntimeClassclass##class_name;\
virtualCRuntimeClass*GetRuntimeClass()const;\
#define_DECLARE_DYNAMIC(class_name)\
public:
\
staticAFX_DATACRuntimeClassclass##class_name;\
virtualCRuntimeClass*GetRuntimeClass()const;\
#endif
//notserializable,butdynamicallyconstructable
#defineDECLARE_DYNCREATE(class_name)\
DECLARE_DYNAMIC(class_name)\
staticCObject*PASCALCreateObject();
#defineDECLARE_SERIAL(class_name)\
_DECLARE_DYNCREATE(class_name)\
friendCArchive&AFXAPIoperator>>(CArchive&ar,class_name*&pOb);
由于这些声明宏都是在CObect派生类的定义中被使用的,所以从这些宏的上述定义中可以看出,DECLARE_DYNAMIC宏给所在类添加了一个CRuntimeClass类型的静态数据成员class##class_name(类名加前缀class,例如,若类名是CPerson,则该变量名称是classCPerson),且指定为const;两个(使用MFCDLL时,否则,一个)成员函数:
虚拟函数GetRuntimeClass和静态函数_GetBaseClass(使用MFCDLL时)。
DECLARE_DYNCREATE宏包含了DECLARE_DYNAMIC,在此基础上,还定义了一个静态成员函数CreateObject。
DECLARE_SERIAL宏则包含了_DECLARE_DYNCREATE,并重载了操作符“>>”(友员函数)。
它和前两个宏有所不同的是CRuntimeClass数据成员class##class_name没有被指定为const。
对应地,MFC使用三个宏初始化DECLARE宏所定义的静态变量并实现DECLARE宏所声明的函数:
IMPLEMNET_DYNAMIC,IMPLEMNET_DYNCREATE,IMPLEMENT_SERIAL。
首先,这三个宏初始化CRuntimeClass类型的静态成员变量class#class_name。
IMPLEMENT_SERIAL不同于其他两个宏,没有指定该变量为const。
初始化内容在下节讨论CRuntimeClass时给出。
其次,它实现了DECLARE宏声明的成员函数:
∙_GetBaseClass()
返回基类的运行时类信息,即基类的CRuntimeClass类型的静态成员变量。
这是静态成员函数。
∙GetRuntimeClass()
返回类自己的运行类信息,即其CRuntimeClass类型的静态成员变量。
这是虚拟成员函数。
对于动态创建宏,还有一个静态成员函数CreateObject,它使用C++操作符和类的缺省构造函数创建本类的一个动态对象。
∙操作符的重载
对于序列化的实现宏IMPLEMENT_SERIAL,还重载了操作符<<和定义了一个静态成员变量
staticconstAFX_CLASSINIT_init_##class_name(RUNTIME_CLASS(class_name));
比如,对CPerson来说,该变量是_init_Cperson,其目的在于静态成员在应用程序启动之前被初始化,使得AFX_CLASSINIT类的构造函数被调用,从而通过AFX_CLASSINIT类的构造函数在模块状态的CRuntimeClass链表中插入构造函数参数表示的CRuntimeClass类信息。
至于模块状态,在后文有详细的讨论。
重载的操作符函数用来在序列化时从文档中读入该类对象的内容,是一个友员函数。
定义如下:
CArchive&AFXAPIoperator>>(CArchive&ar,class_name*&pOb)
{
pOb=(class_name*)ar.ReadObject(
RUNTIME_CLASS(class_name));
returnar;
}
回顾CObject的定义,它也有一个CRuntimeClass类型的静态成员变量classCObject,因为它本身也支持三个特性。
以CObject及其派生类的静态成员变量classCObject为基础,IsKindOf和动态创建等函数才可以起到作用。
这个变量为什么能有这样的用处,这就要分析CRuntimeClass类型变量的结构和内容了。
下面,在讨论了CRuntimeClass的结构之后,考察该类型的静态变量被不同的宏初始化之后的内容。
1.CruntimeClass类的结构与功能
从上面的讨论可以看出,在对CObject特性的支持上,CRuntimeClass类起到了关键作用。
下面,考查它的结构和功能。
1.CRuntimeClass的结构
CruntimeClass的结构如下:
StructCRuntimeClass
{
LPCSTRm_lpszClassName;//类的名字
intm_nObjectSize;//类的大小
UINTm_wSchema;
CObject*(PASCAL*m_pfnCreateObject)();
//pointertofunction,equaltonewclass.CreateObject()
//afterIMPLEMENT
CRuntimeClass*(PASCAL*m_pfnGetBaseClass)();
CRumtieClass*m_pBaseClass;
//operator:
CObject*CreateObject();
BOOLIsDerivedFrom(constCRuntimeClass*pBaseClass)const;
...
}
CRuntimeClass成员变量中有两个是函数指针,还有几个用来保存所在CruntimeClass对象所在类的名字、类的大小(字节数)等。
这些成员变量被三个实现宏初始化,例如:
m_pfnCreateObject,将被初始化指向所在类的静态成员函数CreateObject。
CreateObject函数在初始化时由实现宏定义,见上文的说明。
m_pfnGetBaseClass,如果定义了_AFXDLL,则该变量将被初始化指向所在类的成员函数_GetBaseClass。
_GetBaseClass在声明宏中声明,在初始化时由实现宏定义,见上文的说明。
下面,分析三个宏对CObject及其派生类的CRuntimeClass类型的成员变量class##class_name初始化的情况,然后讨论CRuntimeClass成员函数的实现。
2.成员变量class##class_name的内容
IMPLEMENT_DYNCREATE等宏将初始化类的CRuntimeClass类型静态成员变量的各个域,表3-1列出了在动态类信息、动态创建、序列化这三个不同层次下对该静态成员变量的初始化情况:
表3-1静态成员变量class##class_name的初始化
CRuntimeClass成员变量
动态类信息
动态创建
序列化
m_lpszClassName
类名字符串
类名字符串
类名字符串
m_nObjectSize
类的大小(字节数)
类的大小(字节数)
类的大小(字节数)
m_wShema
0xFFFF
0xFFFF
1、2等,非0
m_pfnCreateObject
NULL
类的成员函数
CreateObject
类的成员函数
CreateObject
m_pBaseClass
基类的CRuntimeClass变量
基类的CRuntimeClass变量
基类的CRuntimeClass变量
m_pfnGetBaseClass
类的成员函数
_GetBaseClass
类的成员函数
_GetBaseClass
类的成员函数
_GetBaseClass
m_pNextClass
NULL
NULL
NULL
m_wSchema类型是UINT,定义了序列化中保存对象到文档的程序的版本。
如果不要求支持序列化特性,该域为0XFFFF,否则,不能为0。
Cobject类本身的静态成员变量classCObject被初始化为:
{"CObject",sizeof(CObject),0xffff,NULL,&CObject:
:
_GetBaseClass,NULL};
对初始化内容解释如下:
类名字符串是“CObject”,类的大小是sizeof(CObject),不要求支持序列化,不支持动态创建。
3.成员函数CreateObject
回顾3.2节,动态创建对象是通过语句pRuntimeClass->CreateObject完成的,即调用了CRuntimeClass自己的成员函数,CreateObject函数又调用m_pfnCreateObject指向的函数来完成动态创建任务,如下所示:
CObject*CRuntimeClass:
:
CreateObject()
{
if(m_pfnCreateObject==NULL)//判断函数指针是否空
{
TRACE(_T("Error:
Tryingtocreateobjectwhichisnot")
_T("DECLARE_DYNCREATE\norDECLARE_SERIAL:
%hs.\n"),
m_lpszClassName);
returnNULL;
}
//函数指针非空,继续处理
CObject*pObject=NULL;
TRY
{
pObject=(*m_pfnCreateObject)();//动态创建对象
}
END_TRY
returnpObject;
}
4.成员函数IsDerivedFrom
该函数用来帮助运行时判定一个类是否派生于另一个类,被CObject的成员函数IsKindOf函数所调用。
其实现描述如下:
如果定义了_AFXDLL则,成员函数IsDerivedFrom调用成员函数m_pfnGetBaseClass指向的函数来向上逐层得到基类的CRuntimeClass类型的静态成员变量,直到某个基类的CRuntimeClass类型的静态成员变量和参数指定的CRuntimeClass变量一致或者追寻到最上层为止。
如果没有定义_AFXDLL,则使用成员变量m_pBaseClass基类的CRuntimeClass类型的静态成员变量。
程序如下所示:
BOOLCRuntimeClass:
:
IsDerivedFrom(
constCRuntimeClass*pBaseClass)const
{
ASSERT(this!
=NULL);
ASSERT(AfxIsValidAddress(this,sizeof(CRuntimeClass),FALSE));
ASSERT(pBaseClass!
=NULL);
ASSERT(AfxIsValidAddress(pBaseClass,sizeof(CRuntimeClass),FALSE));
//simpleSIcase
constCRuntimeClass*pClassThis=this;
while(pClassThis!
=NULL)//从本类开始向上逐个基类搜索
{
if(pClassThis==pBaseClass)//若是参数指定的类信息
returnTRUE;
//类信息不符合,继续向基类搜索
#ifdef_AFXDLL
pClassThis=(*pClassThis->m_pfnGetBaseClass)();
#else
pClass