MFC的RTTI实现机制!
怎么设计RTTI呢?让我们想想,当你看到一种颜色,想知道的它的RGB成分比,不查色表行吗?当你持有一种产品,想知道它的型号,不查型录行吗?要到达RTTI的能力,我们(类库的设计者)一定要在类 构建 起来的时候,记录必要的信息,以建立型录。型录中的类信息,最好以链表方式连接起来,将来方便一一比较。
我们这份“类别型录”的 链表元素 将以CRuntimeClass类来描述,CRuntimeClass类中至少需有类名称、链表的next指针、链表的first指针。由于first指针属于全局变量,所以它应该以static修饰之。除此之外,你所看到的其它CRuntimeClass成员都是为了其它目的而准备,陆陆续续我们会介绍出来。
[c-sharp] view plain copy
-
- struct CRuntimeClass
- {
-
- LPCSTR m_lpszClassName;
- int m_nObjectSize;
- UINT m_wSchema;
- CObject* (PASCAL* m_pfnCreateObject)();
- CRuntimeClass* m_pBaseClass;
-
-
- static CRuntimeClass* pFirstClass;
- CRuntimeClass* m_pNextClass;
- };
我希望,每一个类都能拥有这样一个CRuntimeClass成员变量,并且最好有一定的命名规则(在类名称之前冠以“class”作为它的名称),然后,经由 某种手段 将整个类库构建好之后,每个类中的CRuntimeClass成员变量能相互关联起来。
DECLARE_DYNAMIC / IMPLEMENT_DYNAMIC宏
为了神不知鬼不觉的把CRuntimeClass对象塞到类之中,并声明一个可以抓到该对象地址的函数,我们定义DECLARE_DYNAMIC宏如下:
- #define DECLARE_DYNAMIC(class_name)/
- public: /
- static CRuntimeClass class##classname;
- virtual CRuntimeClass* GetRuntimeClass() const;
-
- 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)
-
- #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;}
-
- #define RUNTIMEC_CLASS(class_name)/
- (&class_name::class##class_name)
-
- struct AFX_CLASSINIT
- {AFX_CLASSINIT(CRuntimeClass* pNewClass);}
- AFX_CLASSINIT::AFX_CLASSINIT(CRuntimeClass* pNewClass)
- {
- pNewClass->m_pNextClass=CRuntimeClass::pFirstClass;
- CRuntimeClass::pFirstClass=pNewClass;
- }
-
于是乎,程序中只需要简简单单的2个宏,DECLARE_DYNAMIC(Cxxx)和IMPLEMENT_DYNAMIC(Cxxx,Cxxxbase)就完成了构建数据并加入链表的工作。
链表的头总是需要特别费心处理,不能够套用一般的链表行为方式。我们的类根源CObject,不能套用现成的宏ECLARE_DYNAMIC和IMPLEMENT_DYNAMIC必须特别设计如下:
[c-sharp] view plain copy
-
- class CObject
- {
- public:
- virtual CRuntimeClass* GetRuntimeClass() const;
- ...
- public:
- static CRuntimeClass classCObject;
- };
-
- 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::pFirstClass=NULL;
-
终于,整个“类别型录”链表的头部就这样形成了。
IsKindOf(类型识别)
有了“类别型录”网,要实现IsKindOf功能,再轻松不过了。
1、为CObject加上一个IsKindOf函数,于是此函数将被所有类继承。该函数将把参数指定的某个CRuntimeClass对象拿来与“类别型录”中的元素一一比较。比较成功(在型录中发现),就传回true,否则传回FALSE:
-
- class CObject
- {
- public:
- BOOL IsKindOf(const CRuntimeClass* pClass) const;
- };
-
- BOOL CObject::IsKindOf(const CRuntimeClass* pClass) const
- {
- CRuntimeClass* pClassThis=GetRuntimeClass();
- while(pClassThis!=NULL)
- {
- if(pClassThis==pClass)
- return true;
- pClassThis=pClassThis->m_pBaseClass;
- }
- return false;
- }
注意: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));
- cout<<pMyDoc->IsKindof(RUNTIME_CLASS(CDocument));
- cout<<pMyDoc->IsKindof(RUNTIME_CLASS(CCmdTarget));
- cout<<pMyDoc->IsKindof(RUNTIME_CLASS(CObject));
- cout<<pMyDoc->IsKindof(RUNTIME_CLASS(CWinApp));
- cout<<pMyDoc->IsKindof(RUNTIME_CLASS(CView));
- cout<<pMyView->IsKindof(RUNTIME_CLASS(CView));
- cout<<pMyView->IsKindof(RUNTIME_CLASS(CObject));
- cout<<pMyView->IsKindof(RUNTIME_CLASS(CWnd));
- cout<<pMyView->IsKindof(RUNTIME_CLASS(CFrameWnd));
Dynamic Creation(动态创建)
分类: 技术-实际工作遇到问题-MFC
2010-08-12 09:53
59人阅读
收藏
举报
基础有了,做什么都好。同样的,有了上述的“类别型录网”,各种应用纷至沓来。其中一个应用就是解决棘手的动态创建问题。前面的文章中已经说过动态创建的困难点:你没有办法在程序执行期间,根据动态获得的一个类名称(通常来自读文件,但我将以屏幕输入为例),要求程序产生一个对象。上述的“类别型录网”虽然透漏出解决此问题的一线曙光,但是技术上还得加把劲。
如果我能够把类的大小记录在“类别型录”中,把构建函数(并非指C++的构造函数,而是指即将出现的CRuntimeClass::CreateObject)也记录在“类别型录”中。当程序在执行期获得一个类名称,它就可以在“类别型录网”中找出对应的元素,然后调用其构建函数,产生出对象。Good Idea!
类别型录网的元素于是有了变化:
-
- struct CRuntimeClass
- {
-
- LPCSTR m_lpszClassName;
- int m_nObjectSize;
- UINT m_wSchema;
- CObject* (PASCAL* m_pfnCreateObject)();
- CRuntimeClass* m_pBaseClass;
- CObject* CreateObject();
- static CRuntimeClass* PASCAL load();
-
- static CRuntimeClass* pFirstClass;
- CRuntimeClass* m_pNextClass;
- };
-
- DECLARE_DYNCREATE
- IMPLEMENT_DYNCREATE
- #define DECLARE_DYNCREATE(class_name)/
- DECLARE_DYNAMIC(class_name)/
- static CObject* PASCAL CreateObject();
-
- #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)
对象创建器CreateObject函数很简单,只要说new就好。从宏的定义中我们可以很清楚的看到,拥有动态创建能力的类库,比如也拥有运行时类型识别的能力,因为_DYNCREATE宏涵盖了_DYNAMIC宏。