MFC的RTTI

来源:互联网 发布:奥尼尔06年总决赛数据 编辑:程序博客网 时间:2024/06/06 02:19

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

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

[c-sharp] view plaincopy
  1. //in MFC.h  
  2. struct CRuntimeClass  
  3. {  
  4.       //Attributes  
  5.       LPCSTR m_lpszClassName;//类名称  
  6.       int m_nObjectSize;//对象大小  
  7.       UINT m_wSchema;  
  8.       CObject* (PASCAL* m_pfnCreateObject)();//函数指针  
  9.       CRuntimeClass* m_pBaseClass;//基类指针  
  10.         
  11.       //CRuntimeClass对象以简单的链表形式链接在一起  
  12.       static CRuntimeClass* pFirstClass;//链表的first指针  
  13.       CRuntimeClass* m_pNextClass;//链表的next指针  
  14. };  
 

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

DECLARE_DYNAMIC / IMPLEMENT_DYNAMIC宏

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

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

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

[cpp] view plaincopy
  1. #define IMPLEMENT_DYNAMIC(class_name,base_class_name)  
  2. _IMPLEMENT_RUNTIMECLASS(class_name,base_class_name,0xFFFF,NULL)  
  3. //其中,_IMPLEMENT_RUNTIMECLASS又是一个宏。这样区分是因为这个宏在“动态创建”时还会用到。  
  4. #define _IMPLEMENT_RUNTIMECLASS(class_name,base_class_name,wSchema,pfnNew)/  
  5. static char _lpsz##class_name[]=#class_name;/  
  6. CRuntimeClass class_name::class##class_name={/  
  7. _lpsz##class_name,sizeof(class_name),wSchema,pfnNew,RUNTIME_CLASS(base_class_name),NULL};/  
  8. static AFX_CLASSINIT _init_##class_name(&class_name::class##class_name);  
  9. CRuntimeClass* class_name::GetRuntimeClass() const;/  
  10. {return &class_name::class##class_name;}  
  11. //其中:RUNTIME_CLASS宏定义如下:  
  12. #define RUNTIMEC_CLASS(class_name)/  
  13. (&class_name::class##class_name)  
  14. //看起来整个IMPLEMENT_DYNAMIC内容好像只是指定初值,其实不然,其美妙处在于它所使用的一个struct AFX_CLASSINIT,定义如下:  
  15. struct AFX_CLASSINIT  
  16. {AFX_CLASSINIT(CRuntimeClass* pNewClass);}//这表示它有一个构造函数(不必惊讶,C++的struct和class都有构造函数),定义如下:  
  17. AFX_CLASSINIT::AFX_CLASSINIT(CRuntimeClass* pNewClass)  
  18. {  
  19.       pNewClass->m_pNextClass=CRuntimeClass::pFirstClass;  
  20.       CRuntimeClass::pFirstClass=pNewClass;  
  21. }  
  22. //很明显,此构造函数负责linked list的链接工作。整组宏看起来有点吓人,文字代换而已,没什么可怕的。  
 

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

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

[c-sharp] view plaincopy
  1. //in header file  
  2. class CObject  
  3. {  
  4. public:  
  5.       virtual CRuntimeClass* GetRuntimeClass() const;  
  6.       ...  
  7. public:  
  8.       static CRuntimeClass classCObject;  
  9. };  
  10. //in implement file  
  11. static char szCObject[]="CObject";  
  12. struct CRuntimeClass CObject::classCObject=  
  13. {szCObject,sizeof(CObject),0xFFFF,NULL,NULL,NULL};  
  14. static AFX_CLASSINIT _init_CObject(&CObject::classCObject);  
  15. CRuntimeClass* CObject::GetRuntimeClass const  
  16. {  
  17.       return &CObject::classCObject;  
  18. }  
  19. //并且CRuntimeClass中的静态成员需要初始化  
  20. CRuntimeClass* CRuntimeClass::pFirstClass=NULL;  
  21. //这样,“类别型录”链表的头部就形成了。  
 

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

IsKindOf(类型识别)

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

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

[cpp] view plaincopy
  1. //in header file  
  2. class CObject  
  3. {  
  4. public:  
  5.       BOOL IsKindOf(const CRuntimeClass* pClass) const;  
  6. };  
  7. //in implementation file  
  8. BOOL CObject::IsKindOf(const CRuntimeClass* pClass) const  
  9. {  
  10.       CRuntimeClass* pClassThis=GetRuntimeClass();  
  11.       while(pClassThis!=NULL)  
  12.       {  
  13.             if(pClassThis==pClass)  
  14.                   return true;  
  15.             pClassThis=pClassThis->m_pBaseClass;  
  16.       }  
  17.       return false;//walked to the top,no match  
  18. }  
 

注意: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的使用方法如下:

[cpp] view plaincopy
  1. CMyDoc* pMyDoc=new CMyDoc;  
  2. CMyView* pMyView=new CMyView;  
  3. cout<<pMyDoc->IsKindof(RUNTIME_CLASS(CMyDoc));//true  
  4. cout<<pMyDoc->IsKindof(RUNTIME_CLASS(CDocument));//true  
  5. cout<<pMyDoc->IsKindof(RUNTIME_CLASS(CCmdTarget));//true  
  6. cout<<pMyDoc->IsKindof(RUNTIME_CLASS(CObject));//true  
  7. cout<<pMyDoc->IsKindof(RUNTIME_CLASS(CWinApp));//false  
  8. cout<<pMyDoc->IsKindof(RUNTIME_CLASS(CView));//false  
  9. cout<<pMyView->IsKindof(RUNTIME_CLASS(CView));//true  
  10. cout<<pMyView->IsKindof(RUNTIME_CLASS(CObject));//true  
  11. cout<<pMyView->IsKindof(RUNTIME_CLASS(CWnd));//true  
  12. cout<<pMyView->IsKindof(RUNTIME_CLASS(CFrameWnd));//false  
 

0 0