一、MFC程序框架设计概述
1、MFC类层次关系
MFC程序也是Windows程序,它的内部实现必然也有窗口注册,窗口产生,消息循环,窗口回调过程。
CObject | |||
CCmdTarget | |||
CWinThread | |||
CWinThread | |||
CWnd | |||
CView | |||
CFrameWnd | |||
CDocument |
二、运行时类信息(CRuntimeClass类)
在程序运行的过程中辨别对象是否属于特定类的技术叫动态类型识别(Runtime Type Infomation,RTTI)。
当函数需要识别其参数类型的时候,或者是必须针对对象所属的类编写所属目的的代码时,运行期识别就变得非常有用了。
框架程序使用这一技术管理应用程序是非常方便的,因为用户可以任意设计自己的类,框架程序可以在程序执行的时候知道这个类的类名、大小等信息,并能创建这个类的对象。
如何识别对象是否属于某个类呢?区别进程可以使用进程ID,区别窗口可以使用窗口句柄,要区别类的话,必须也要给类安排惟一的标识。类的静态成员不属于对象的一部分,而是类的一部分,也就是说在定义类的时候,编译器已经为这个类的静态成员分配内存了,不管实例化多少个此类的对象,类的静态成员在内存中都只有一份。因此,可以给每个类安排一个静态成员变量,此成员变量的内存地址就是这个类的标识!
静态成员类型为int,其值并没有实际意义。为了在运行期间记录类的信息,可以用有意义的结构来描述此静态成员,将之命名为CRuntimeClass。类的最基本信息包括类的名称、大小。
LPCSTR m_lpszClassName; // 类的名字
int m_n0bjectSize; // 类的大小
在系统升级的过程中,类的成员很可能会发生变化,所以还要有类的版本号。
UINT m_wSchema; // 类的版本号
如果为每个类都写一个创建该类的全局函数的话,就能够依靠从文件或用户的输入中取得此函数的内存地址,从而创建用户动态指定的类,即动态创建。即有:
CObject* (__stdcall* m_pfnCreateObject)(); // 创建类的函数的指针
CObject* CreateObject() ;
要想使所有的类都具有运行期识别和动态创建的特性,必须有一个类做为继承体系的顶层,也就是说所有具有此特性的类都要从一个类继承,所以有了顶层的Cobject的类设计。
下面是完整的CRuntimeClass结构
测试代码:
_afx.h
OBJCORE.cpp
test.cpp // 测试RTTI
RUNTIME_CLASS是为了方便访问类的CRuntimeClass结构而定义的宏。在这里可以看到每个类中 CRuntimeClass成员变量的命名规则:在类名之前冠以class作为它的名字。
class##class_name中的##告诉编译器,把两个字符串捆在一起。GetRuntimeClass函数就使用了RUNTIME_CLASS宏。
pClass指针指向的是CBoy类的CRuntimeClass对象。
如果要想使CBoy类也支持动态创建,只需要在初始化 classCBoy对象的时候传递一个创建CBoy对象的函数的地址就行了。很简单,为CBoy类再安排一个静态成员函数,此成员函数就负责创建CBoy对象,名称为CreateObject。
为了将CreateObject函数传给classCBoy对象中的m_pfnCreateObject成员,修改该对象的初始化代码如下。
const CRuntimeClass CBoy::classCBoy =
{
"CBoy",
sizeof(CBoy),
Oxffff,
&CBoy::CreateObject,
(CRuntimeClass*)&CObject::classCObject,
NULL
};
支持类的运行期识别能力的代码是固定的:一个CRuntimeClass类型的静态变量,一个可以取得该对象地址的虚函数GetRuntimeClass。为了方便用户使用,最好设计一组宏来代替这些重复性代码。比如,在定义类的时候,用名称为DECLARE_DYNAMIC的宏来代替变量的定义和函数的声明。
#define DECLARE_DYNAMIC(class_name) \ //声明支持动态类信息
public: \
static const CRuntimeClass class##class_name; \
virtual CRuntimeClass* GetRuntimeClass() const;
与DECLARE(声明)相对应的就是IMPLEMENT(实现)了,所以再定义下面两个宏来代替初始化CRuntimeClass对象的代码和实现GetRuntimeClass 函数的代码。提供 IMPLEMENT_RUNTIMECLASS宏完全是为了方便,在实现不同的功能的时候直接给该宏传递不同的初始化参数就行了。例如,运行期识别的功能只对当前类的类名class_name和基类的类名base_class_name有要求,所以IMPLEMENT_DYNAMIC宏只有这两个参数,然后再添加上其他的默认值去调用IMPLEMENT_RUNTIMECLASS宏。
现在只需要使用DECLARE_DYNAMIC和IMPLEMENT_DYNAMIC两个宏就可以使CObject的派生类拥有动态识别的功能。
完成中。。。来源于MFC工程应用开发与框架原理剖析
三、MFC中的映射:框架中内存分配技术---句柄映射
四、隐藏WinMain的实现---框架的初始化与声明周期