MFC的RTTI实现机制!

      怎么设计RTTI呢?让我们想想,当你看到一种颜色,想知道的它的RGB成分比,不查色表行吗?当你持有一种产品,想知道它的型号,不查型录行吗?要到达RTTI的能力,我们(类库的设计者)一定要在类 构建 起来的时候,记录必要的信息,以建立型录。型录中的 类信息,最好以链表方式连接起来,将来方便一一比较。

      我们这份“类别型录”的 链表元素 将以CRuntimeClass类来描述,CRuntimeClass类中至少需有类名称、链表的next指针、链表的first指针。由于first指针属于全局变量,所以它应该以static修饰之。除此之外,你所看到的其它CRuntimeClass成员都是为了其它目的而准备,陆陆续续我们会介绍出来。

//in MFC.h struct CRuntimeClass { //Attributes LPCSTR m_lpszClassName;//类名称 int m_nObjectSize;//对象大小 UINT m_wSchema; CObject* (PASCAL* m_pfnCreateObject)();//函数指针 CRuntimeClass* m_pBaseClass;//基类指针 //CRuntimeClass对象以简单的链表形式链接在一起 static CRuntimeClass* pFirstClass;//链表的first指针 CRuntimeClass* m_pNextClass;//链表的next指针 }; 

      我希望,每一个类都能拥有这样一个CRuntimeClass成员变量,并且最好有一定的命名规则(在类名称之前冠以“class”作为它的名称),然后,经由 某种手段 将整个类库构建好之后,每个类中的CRuntimeClass成员变量能相互关联起来。

DECLARE_DYNAMIC / IMPLEMENT_DYNAMIC宏

      为了神不知鬼不觉的把CRuntimeClass对象塞到类之中,并声明一个可以抓到该对象地址的函数,我们定义DECLARE_DYNAMIC宏如下:

#define DECLARE_DYNAMIC(class_name)/ public: / static CRuntimeClass class##classname; virtual CRuntimeClass* GetRuntimeClass() const; //出现在宏定义中的##,用来告诉编译器,把2个字符串系在一起,如果你这么使用此宏: DECLARE_DYNAMIC(CView),编译器为你做出的代码是: public: / static CRuntimeClass classCView; virtual CRuntimeClass* GetRuntimeClass() const; 

这下子,只要在声明类时放入DECLARE_DYNAMIC宏即万事OK喽。不,还没有OK,类别型录的内容指定以及链接工作最好也能够神不知鬼不觉的,于是我们再定义IMPLEMENT_DYNAMIC宏:

#define IMPLEMENT_DYNAMIC(class_name,base_class_name) _IMPLEMENT_RUNTIMECLASS(class_name,base_class_name,0xFFFF,NULL) //其中,_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宏定义如下: #define RUNTIMEC_CLASS(class_name)/ (&class_name::class##class_name) //看起来整个IMPLEMENT_DYNAMIC内容好像只是指定初值,其实不然,其美妙处在于它所使用的一个struct AFX_CLASSINIT,定义如下: struct AFX_CLASSINIT {AFX_CLASSINIT(CRuntimeClass* pNewClass);}//这表示它有一个构造函数(不必惊讶,C++的struct和class都有构造函数),定义如下: AFX_CLASSINIT::AFX_CLASSINIT(CRuntimeClass* pNewClass) { pNewClass->m_pNextClass=CRuntimeClass::pFirstClass; CRuntimeClass::pFirstClass=pNewClass; } //很明显,此构造函数负责linked list的链接工作。整组宏看起来有点吓人,文字代换而已,没什么可怕的。  

      于是乎,程序中只需要简简单单的2个宏,DECLARE_DYNAMIC(Cxxx)和IMPLEMENT_DYNAMIC(Cxxx,Cxxxbase)就完成了构建数据并加入链表的工作。

      链表的头总是需要特别费心处理,不能够套用一般的链表行为方式。我们的类根源CObject,不能套用现成的宏ECLARE_DYNAMIC和IMPLEMENT_DYNAMIC必须特别设计如下:

//in header file class CObject { public: virtual CRuntimeClass* GetRuntimeClass() const; ... public: static CRuntimeClass classCObject; }; //in implement file static char szCObject[]="CObject"; struct CRuntimeClass CObject::classCObject= {szCObject,sizeof(CObject),0xFFFF,NULL,NULL,NULL}; static AFX_CLASSINIT _init_CObject(&CObject::classCObject); CRuntimeClass* CObject::GetRuntimeClass const { return &CObject::classCObject; } //并且CRuntimeClass中的静态成员需要初始化 CRuntimeClass* CRuntimeClass::pFirstClass=NULL; //这样,“类别型录”链表的头部就形成了。 

终于,整个“类别型录”链表的头部就这样形成了。

IsKindOf(类型识别)

      有了“类别型录”网,要实现IsKindOf功能,再轻松不过了。

1、为CObject加上一个IsKindOf函数,于是此函数将被所有类继承。该函数将把参数指定的某个CRuntimeClass对象拿来与“类别型录”中的元素一一比较。比较成功(在型录中发现),就传回true,否则传回FALSE:

//in header file class CObject { public: BOOL IsKindOf(const CRuntimeClass* pClass) const; }; //in implementation file BOOL CObject::IsKindOf(const CRuntimeClass* pClass) const { CRuntimeClass* pClassThis=GetRuntimeClass(); while(pClassThis!=NULL) { if(pClassThis==pClass) return true; pClassThis=pClassThis->m_pBaseClass; } return false;//walked to the top,no match }  

注意:while循环中所追踪的是“同宗”路线,也就是凭着m_pBaseClass而非m_pNextClass,假设我们的调用是:CView* pView=new CView;      pView->IsKindOf( RUNTIME_CLASS(CWinApp) );,其中RUNTIME_CLASS是一个宏,替换后为&CWinApp::classCWinApp。函数内部利用GetRuntimeClass先取得&CView::classCView,然后巡线而上(所谓巡线分别是指CView、CWnd、CCmdTarget、CObject),没获得一个CRuntimeClass对象指针,就拿来和CView::classCView的指针比较,靠这个土方法,完成了IsKindOf的能力。

2、IsKindOf的使用方法如下:

CMyDoc* pMyDoc=new CMyDoc; CMyView* pMyView=new CMyView; cout<<pMyDoc->IsKindof(RUNTIME_CLASS(CMyDoc));//true cout<<pMyDoc->IsKindof(RUNTIME_CLASS(CDocument));//true cout<<pMyDoc->IsKindof(RUNTIME_CLASS(CCmdTarget));//true cout<<pMyDoc->IsKindof(RUNTIME_CLASS(CObject));//true cout<<pMyDoc->IsKindof(RUNTIME_CLASS(CWinApp));//false cout<<pMyDoc->IsKindof(RUNTIME_CLASS(CView));//false cout<<pMyView->IsKindof(RUNTIME_CLASS(CView));//true cout<<pMyView->IsKindof(RUNTIME_CLASS(CObject));//true cout<<pMyView->IsKindof(RUNTIME_CLASS(CWnd));//true cout<<pMyView->IsKindof(RUNTIME_CLASS(CFrameWnd));//false 

 

你可能感兴趣的:(MFC的RTTI实现机制!)