要分析MFC的实现首先要从MFC当中众多的宏开始讨论。首先看一个上次讲到的宏:
#define RUNTIME_CLASS(class_name) ((CRuntimeClass*)(&class_name::class##class_name))这个宏的实现很简单,就是返回一个类当中的一个成员变量的地址,这个成员变量要么是静态成员变量,要么是虚拟函数。因为用命名空间符提取只能用于这两种情况。先留下疑问,看看CRuntimeClass的定义。
struct CRuntimeClass { LPCSTR m_lpszClassName; //存放类的名称 int m_nObjectSize; //类的尺寸大小 UINT m_wSchema; // 一个标志符号 CObject* (PASCAL* m_pfnCreateObject)(); //如果这个函数为空,则表明类是一个虚拟类,不能有实例 CRuntimeClass* m_pBaseClass; //基类的CRuntimeClass CObject* CreateObject(); //创建 BOOL IsDerivedFrom(const CRuntimeClass* pBaseClass) const;//判断两个类别是否具有继承关系 void Store(CArchive& ar) const; //用于存放类别信息 static CRuntimeClass* PASCAL Load(CArchive& ar, UINT* pwSchemaNum);//用于加载类别信息 CRuntimeClass* m_pNextClass; // 将整个CRuntimeClass类别链成一个链表 }; *************************************************************** /*下面这个宏展开之后,定义了一个与类名息息相关的静态CRuntimeClass成员变量,另外定义了一个虚拟函数用于获取相应类的CRuntimeClass*/ #define DECLARE_DYNAMIC(class_name) \ public: \ static const AFX_DATA CRuntimeClass class##class_name; \ virtual CRuntimeClass* GetRuntimeClass() const; \ ********************************************************************* /*用于对CRuntimeClass结构体进行填充,由于后面的成员函数并不是指针类型的,所以不需要填充。同时实现上面的GetRuntimeClass函数*/#define IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, wSchema, pfnNew) \
AFX_COMDAT const AFX_DATADEF CRuntimeClass class_name::class##class_name = { \
#class_name, sizeof(class class_name), wSchema, pfnNew, \
RUNTIME_CLASS(base_class_name), NULL }; \
CRuntimeClass* class_name::GetRuntimeClass() const \
{ return RUNTIME_CLASS(class_name); } \
***************************************************************
#define IMPLEMENT_DYNAMIC(class_name, base_class_name) \
IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, 0xFFFF, NULL)
通过上面的分析,不难看出,每个类都有一个类似于管家的结构体,这个结构体包含自身的信息,并且这个信息在整个程序当中是唯一的,同时这个结构体包含基类的的RuntimeClass的指针。
#define DECLARE_DYNCREATE(class_name) \ DECLARE_DYNAMIC(class_name) \ static CObject* PASCAL CreateObject(); *******************************************************************这个宏的实现相对于之前的更进一步了,定义一个静态的成员函数,这个函数的实现是生成一个类别,而这个类别有CObject*指向,很很明显class_name是CObejct的子类别。并且在实现当中将静态成员函数传递到CRuntimeClass结构体当中,这样的话,CRuntimeClass类别就具有生成class_name类别的所有信息了。#define IMPLEMENT_DYNCREATE(class_name, base_class_name) \
CObject* PASCAL class_name::CreateObject() \
{ return new class_name; } \
IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, 0xFFFF, \
class_name::CreateObject)
#define DECLARE_SERIAL(class_name) \ _DECLARE_DYNCREATE(class_name) \ AFX_API 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) \ 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; } \
上面的CRuntimeClass将所有的从CObject类继承来的类别连接一个链表,这样就可以在父类和子类之间传递消息,直到消息被其中某一个类处理或者所有的类都不处理,那么就到了默认的处理函数,也就是DefWindowProc。
BOOL CRuntimeClass::IsDerivedFrom(const CRuntimeClass* pBaseClass) const { const CRuntimeClass* pClassThis = this; while (pClassThis != NULL) { if (pClassThis == pBaseClass) return TRUE; pClassThis = pClassThis->m_pBaseClass; } return FALSE; } CObject* CRuntimeClass::CreateObject() { if (m_pfnCreateObject == NULL) { return NULL; } CObject* pObject = NULL; TRY { pObject = (*m_pfnCreateObject)(); } END_TRY return pObject; }从上面的实实现可以看出,由于整个程序当中一个类对应一个CRuntimeClass,所以这里的数据是唯一的,如果某一个类是另外一个类的基类,那么通过回溯整个CRuntimeClass类总会有一个指针域pBaseClass相等。而下面的CreateObject则给了一个用户处理的机会。