MFC六大关键技术之仿真学习笔记(三)

来源:互联网 发布:手机淘宝店怎么开不了 编辑:程序博客网 时间:2024/05/16 18:11

       如果你感觉前面两节一帆风顺的话,或许从第三节开始,你会感觉理解有一点点困难,或许读完整节,你只能MFC说有这么一个东西,但是并不明白这样设计的好处或如何去使用,别急,让我们慢慢来学习它。


*RTTI(运行时类型识别)

       初看RTTI(Runtime Type Identification),我并不是很明白这个玩意的作用,书中有这样的一种比方:当你看到一种颜色,想知道它的RGB值,不查表可以吗?当有你有一种商品,想知道它的型号不差目录行吗?算是明白了,这不仅是一个类类型判断问题,也涉及到如何保存这些类信息的问题,即引入一个“类别型录”的链表,这里链表元素以CRuntimeClass来描述。

struct CRuntimeClass{//AttributesLPCSTR m_lpszClassName;  //类名int m_nObjectSize;   //对象大小UINT m_wSchema;   //模式号,如果不要求支持序列化特性,该域为0xFFFF,否则,不能为0;CObject *(PASCAL *m_pfnCreateObject)(); CRuntimeClass *m_pBaseClass;static CRuntimeClass* pFirstClass;  CRuntimeClass* m_pNextClass; };
结构如上,我们开始分析

(1) CObject *(PASCAL *m_pfnCreateObject)(); 定义一个返回CObject* 的函数指针,根据函数指针命名,我们可以知道,它完成的是CreateObject的操作,即一个CRuntimeClass我们就可以产生对应的对象。

(2) CRuntimeClass *m_pBaseClass;  定义一个CRuntimeClass指针指向基类CRuntimeClass,在链表中不仅有前后关系,我们也要实现纵向的继承关系。

(3) static CRuntimeClass *pFirstClass; 定义一个静态成员变量指向链表第一个CRuntimeClass,静态成员变量在产生对象之前进行初始化,并不依赖对象的构造和析构,并且在多个对象中此值唯一

(4) CRuntimeClass* m_pNextClass; 指针指向链表中下一个元素。


介绍完CRuntimeClass,我们来看看我们最终应该实现的效果:


是的,我们需要一种简便又快捷的方式实现上述链表,那么我们该怎么办?至此,我们需要用来实现这些关键步骤,让CRuntimeClass被神不知鬼不觉的塞入我们的类中。当然,别担心,宏只是简单的文本替换。我们来看看具体实现步骤:

(1)MFC定义了一个叫DECLARE_DYNAMIC (也就是动态声明的意思)的宏:

#define DECLARE_DYNAMIC(class_name) \public: \static CRuntimeClass class##class_name; \virtual CRuntimeClass *GetRuntimeClass() const;
比如,我们在CWnd中加入宏定义如下:(此处##是讲两个字符串连接到一起)

class CWnd:public CCmdTarget{DECLARE_DYNAMIC(CWnd)}
实际上我们加入的是:

class CWnd:public CCmdTarget{public:static CRuntimeClass classCWnd; virtual CRuntimeClass* GetRuntimeClass() const;}
我们来分析以上结构:
(1) static CRuntimeClass classCWnd : static变量保存一个对应的CRuntimeClass,这样系统初始化时应该会存在一一对应关系进行初始化,这样我们可以通过CRuntimeClass轻松找到我们需要用的类。

(2) virtual CRuntimeClass* GetRuntimeClass() const :虚函数子类可进行Override,这样的好处是,我们可以轻松找到子类对CRuntimeClass的最新override,不管我们当时是否使用的基类指针(虚函数是根据虚函数表进行读取的)。


这也就是虚函数的魅力所在吧!当然,声明完以后,我们需要去实现它,我们进一步引进了IMPLEMENT_DYNAMIC宏,如下:

#define IMPLEMENT_DYNAMIC(class_name,base_class_name) \_IMPLEMENT_RUNTIMECLASS(class_name,base_class_name,0xFFFF,NULL)
IMPLEMENT_DYNAMIC是对_IMPLEMENT_RUNTIMECLASS进行的封装,来看看_IMPLEMENT_RUNTIMECLASS的代码:

#define _IMPLEMENT_RUNTIMECLASS(class_name,base_class_name,wSchema,pfnNew) \static char lpsz##class_name[] = #class_name; \CRuntimeClass class_name::class##class_name = { \lpsz##class_name,sizeof(class_name),wSchema,pfnNew, \RUNTIME_CLASS(base_class_name),NULL }; \static AFX_CLASSINIT _init_##class_name(&class_name::class##class_name); \CRuntimeClass* class_name::GetRuntimeClass() const \{ return &class_name::class##class_name;} \

涉及到RUNTIME_CLASS(class_name)宏:

#define RUNTIME_CLASS(class_name) \(&class_name::class##class_name)

涉及到一个AFX_CLASSINIT结构体(运用结构体的构造函数实现CRuntimeClass链表连接)

struct AFX_CLASSINIT{AFX_CLASSINIT(CRuntimeClass* pNewClass);{   pNewClass->m_pNextClass = CRuntimeClass::pFirstClass;   CRuntimeClass::pFirstClass = pNewClass;}};
比如我们同样用CWnd来做示范:

IMPLEMENT_DYNAMIC(CWnd, CCmdTarget)
我们拆解开看看是啥:
static char lpszCWnd[] = "CWnd";struct CRuntimeClass CWnd::classCWnd = { lpszCWnd, sizeof(szCWnd),wSchema,pfnNew,RUNTIME_CLASS(CCmdTarget),NULL};static AFX_CLASSINIT _init_CWnd(&CWnd::classCWnd);CRuntimeClass* CWnd::GetRuntimeClass() const   { return &CWnd::classCWnd;}

以上即实现了DECLARE_DYNAMIC中定义的成员和函数,并将链表增长。


对于类根源CObject,我们进行如下处理形成头部:

class CObject{public:virtual CRuntimeClass* GetRuntimeClass() const;public:static CRuntimeClass classCObject;};static char lpszCObject[] = "CObject";struct CRuntimeClass CObject::classCObject = { lpszCObject, sizeof(szCObject), 0xFFFF, NULL, NULL, NULL };static AFX_CLASSINIT _init_CObject(&CObject::classCObject);CRuntimeClass* CRuntimeClass::pFirstClass = NULL;CRuntimeClass* CObject::GetRuntimeClass() const{return &CObject::classCObject;}


运用DECLARE_DYNAMIC和IMPLEMENT_DYNAMIC完成了CRuntimeClass链表的实现。我们可以通过对链表的遍历来知道类之间的层次和前后关系,对后来的动态创建和类型识别有重要的作用,这不愧是MFC的精华和关键啊!





0 0