5.3 MFC的关键技术
MFC的关键技术包括:MFC程序的初始化过程、运行时的类型识别RTTI、动态创建、序列化、消息映射及命令路由6种,下面将对这些技术的功能和实现机制进行详细介绍。
5.3.1 RTTI
视频精讲:光盘\video\05\RTTI.swf
RTTI(Run-Time Type Identification)即运行时类型识别,该项技术使得应用程序能够使用基类的指针或引用来检查它们所指对象的实际派生类型。每个从CObject派生的类都有一个静态的CRuntimeClass结构的对象同它关联以保存类的一些基本信息,实现在运行的过程中获得类实例或其基类信息的功能。表5-2是关于CRuntimeClass中比较重要的一些成员的介绍。
表5-2 CRuntimeClass中一些重要成员的介绍
|
成员名< xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" /> |
成员含义 |
|
m_lpszClassName |
代表类名字符串 |
|
m_nObjectSize |
代表对象的大小 |
|
m_wSchema |
被加载类的schema号 |
|
m_pfnCreateObject |
指向具有动态创建对象功能函数的指针 |
|
m_pBaseClass |
指向基类CRuntimeClass结构的指针 |
|
m_pfnGetBaseClass |
指向类的成员函数_GetBaseClass,返回CRuntimeClass结构 |
光有CRuntimeClass结构还不够,为了实现运行时类型识别,还需要执行以下几步。
(1)令需要添加运行时类信息支持的类派生于CObject类。
(2)在类的声明中添加DECLARE_DYNAMIC()宏。
(3)在类的实现文件中添加IMPLEMENT_DYNAMIC()宏。
执行上述3步操作之后,代码的形式如下:
//*.h //为C***Class类添加运行时类信息支持 class C***Class : public CFrameWnd { DECLARE_DYNAMIC(C***Class) …… }; //*.cpp IMPLEMENT_DYNAMIC(C***Class, CObject) |
其中,宏DECLARE_DYNAMIC()的作用是提供基本的运行时类型识别声明。在afx.h头文件下,宏DECLARE_DYNAMIC()的定义如下:
#define DECLARE_DYNAMIC(class_name) \ protected: \ static CRuntimeClass* PASCAL _GetBaseClass(); \ public: \ static const CRuntimeClass class##class_name; \ static CRuntimeClass* PASCAL GetThisClass(); \ virtual CRuntimeClass* GetRuntimeClass() const; \
|
因此,在进行宏替换之后,上述代码中DECLARE_DYNAMIC()为C***Class类所执行的操作实际上为:
protected: static CRuntimeClass* _GetBaseClass(); public: static const CRuntimeClass classC***Class; static CRuntimeClass* GetThisClass(); virtual CRuntimeClass* GetRuntimeClass() const;
|
在上述代码中值得注意的是,classC***Class是一个静态常量,即它在所有的类对象中是唯一的,因此,通过对比这个成员所指向的地址便可实现对两个对象是否属于同一类型的判断。
而宏IMPLEMENT_DYNAMIC()的作用则是初始化类的CRuntimeClass对象的各个域,提供基本的运行时类型识别实现,其定义如下:
#define IMPLEMENT_DYNAMIC(class_name, base_class_name) \ IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, 0xFFFF, NULL, NULL) |
其中,IMPLEMENT_RUNTIMECLASS()宏调用的定义如下:
#define IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, wSchema, pfnNew, class_init) \ CRuntimeClass* PASCAL class_name::_GetBaseClass() \ { return RUNTIME_CLASS(base_class_name); } \ AFX_COMDAT const CRuntimeClass class_name::class##class_name = { \ #class_name, sizeof(class class_name), wSchema, pfnNew, \ &class_name::_GetBaseClass, NULL, class_init }; \ CRuntimeClass* PASCAL class_name::GetThisClass() \ { return _RUNTIME_CLASS(class_name); } \ CRuntimeClass* class_name::GetRuntimeClass() const \ { return _RUNTIME_CLASS(class_name); } |
因此,在进行宏替换之后,上述代码中IMPLEMENT_DYNAMIC()为C***Class类所执行的操作实际上为:
//获得基类的CRuntimeClass对象 CRuntimeClass* C***Class::_GetBaseClass() { return (CRuntimeClass*)(&CObject::classCObject); } //初始化classC***Class的成员并保存当前类的名称、大小、模式、创建函数的调用地址等 CRuntimeClass C***Class::class C***Class = { "C***Class", sizeof(class C***Class), 0xFFFF, NULL, & C***Class::_GetBaseClass, NULL, NULL }; //得到类的CRuntimeClass对象 CRuntimeClass* C***Class::GetThisClass() { return (CRuntimeClass*)(&C***Class::classC***Class); } CRuntimeClass* C***Class::GetRuntimeClass() const { return (CRuntimeClass*)(&C***Class::classC***Class); } |
至此,我们可以看出,MFC为支持运行时类型识别所采用的机制是:首先,为每个类添加一个静态的CRuntimeClass对象,在该对象中保存了类的相关信息,包括类名、类结构的大小、创建函数的入口地址等;其次,当判断某个对象是否属于某个类时,只需要将该对象的CRuntimeClass成员与指定类的CRuntimeClass成员进行比较,或是通过_GetBaseClass()得到基类的CRuntimeClass对象并进行判断即可。CObject类的成员函数IsKindOf()可用于执行这种运行时的类型识别,函数内部调用了CRuntimeClass::IsDerivedFrom(),其关键代码如下:
const CRuntimeClass* pClassThis = this; #ifdef _AFXDLL for (;;) #else while (pClassThis != NULL) #endif { //通过CRuntimeClass对象进行类型比较,若相同则返回true if (pClassThis == pBaseClass) return TRUE; #ifdef _AFXDLL //获得基类的CRuntimeClass对象 if (pClassThis->m_pfnGetBaseClass == NULL) break; pClassThis = (*pClassThis->m_pfnGetBaseClass)(); #else pClassThis = pClassThis->m_pBaseClass; #endif } //类型不匹配,返回false return FALSE; |
【责任编辑:
云霞 TEL:(010)68476606】