MFC早在编译器支持RTTI(RunTime Type Information)之前,就具有这种能力。
类别型录网与CruntimeClass
若要达到RTTI的能力,类库的设计者一定要在类构建时,记录必要的信息,以建立型录。型录中的信息,最好以链表(linked list)方式连接,将来方便比较。
这份“类别型录”的链表元素将以CruntimeClass描述之,这个结构中至少应该有类的名称,链表的Next指针和First指针。由于First指针属于全局变量,所以它应该以Static修饰。
Struct CruntimeClass
{
//Attributes
LRCSTR m_lpnzClassName;
Int m_nObjectSize;
UINT m_wSchema; //schema number of the loaded class
CObject *(PASCAL * m_pfnCreateObject)();//NULL=>abstract class
CRuntimeClass *m_pBaseClass;
//CruntimeClass objects linked together in the simple list
static CRuntimeClass *pFirstClass;
CRuntimeClass * m_pNextClass;
}
我们希望,每一个类都能拥有这样一个CRuntimeClass成员变量,并且最好有一定的命名规则,然后,经由某中手段将整个类库构建好。
DECLARE_DYNAMIC/IMPLEMENT_DYNAMIC宏
为了不知不觉的把CRuntimeClass对象放到类之中,并声明一个可以取到该对象地址的函数,定义DECLARE_DYNAMIC宏如下:
#define DECLARE_DYNAMIC(class_name) /
#define DECLARE_DYNAMIC(class_name) /
public: /
static CRuntimeClass class##class_name; /
virtual CRuntimeClass * GetRuntimeClass() const;
如果这样使用宏: DECLARE_DYNAMIC(CView)
编译器处理后的做出的代码是:
public:
static CRuntimeCLass classCView;
virtual CRuntimeClass * GetRuntimeClass() const;
类别型录的内容的指定和链接最好也能够不知不觉,于是定义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##classname[] = #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 RUNTIME_CLASS(class_name) /
#define RUNTIME_CLASS(class_name) /
(&class_name::class##class_name)
IMPLEMENT_DYNAMIC的美妙之处在于它所使用的一个struct AFX_CLASSINIT,定义如下:
Struct AFX_CLASSINIT
{ AFX_CLASSINIT(CRuntimeClass *pNewClass);};
它的构造函数定义为:
AFX_CLASSINIT::AFX_CLASSINIT(CRuntimeClass * pNewClass)
AFX_CLASSINIT::AFX_CLASSINIT(CRuntimeClass * pNewClass)
{
pNewClass->m_pNextclass=CRuntimeClass ::pFirstClass;
CRuntimeClass::pFirstClass = pNewClass;
}
此构造函数负责linked list的连接工作。
看个例程:
class Cview : public CWnd
{
DECLARE_DYNNAMIC(CView)
…
};
IMPLEMENT_DYNAMIC(Cview,CWnd)
上述的代码展开后成为:
class Cview : public CWnd
{
public:
static CRuntimeClass classCView;/
virtual CRuntimeClass * GetRuntimeClass() const;
…
};
static char _lpszCView[] =
“
Cview
”
;
CRuntimeClass Cview::classView = {
_lpszCView,sizeof(CView),0XFFFF,NULL,&CWnd::ClassCWnd,NULL};
static AFX_CLASSINIT _init_Cview(&Cview::classCView);
CRuntimeClass *Cview::GetRuntimeClass() const
{return &Cview::classCView;}
于是,程序中只需要简简单单的两个宏,就完成了构建数据并加入链表的工作。
链表头需要特殊处理,不能套用一般的链表行为方式。我们的类根源CObject,不能套用现成的宏DECLARE_DYNAMIC和IMPLEMENT_DYNAMIC,必须特殊设计如下:
class CObject
{
public:
virtual Cruntime * GetRuntimeClass() const;
virtual Cruntime * 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;
范例程序
MFC.H
#define BOOL int
#define TURE 1
#define FALSE 0
#define LPCSTR LPSTR
#define char * LPSTR;
#define UINT int
#define PASCAL _stdcall
#include <iostream.h>
class CObject
Struct CruntimeClass
{
//Attributes
LRCSTR m_lpnzClassName;
Int m_nObjectSize;
UINT m_wSchema; //schema number of the loaded class
CObject *(PASCAL * m_pfnCreateObject)();//NULL=>abstract class
CRuntimeClass *m_pBaseClass;
//CruntimeClass objects linked together in the simple list
static CRuntimeClass *pFirstClass;
CRuntimeClass * m_pNextClass;
}
Struct AFX_CLASSINIT
{ AFX_CLASSINIT(CRuntimeClass *pNewClass);};
#define RUNTIME_CLASS(class_name) /
(&class_name::class##class_name)
#define DECLARE_DYNAMIC(class_name) /
public: /
static CRuntimeClass class##class_name; /
virtual CRuntimeClass * GetRuntimeClass() const;
#define _IMPLEMENT_RUNTIMECLASS(class_name,base_class_name,wSchema,pfnNew) /
static char lpsz##classname[] = #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 IMPLEMENT_DYNAMIC(class_name , base_class_name) /
_IMPLEMENT_RUNTIMECLASS(class_name,base_class_name,0xFFFF,NULL)
class CObject
{
public:
CObject::CObject() { }
CObject::~CObject() { }
Virtual CRuntimeClass *GetRuntimeClass() const;
Public:
static CRuntimeClass classCObject;
};
class CCmdTarget : public CObject
{
DECLARE_DYNAMIC(CCmdTarget)
public:
CCmdTarget:: CCmdTarget () { }
CCmdTarget::~CCmdTarget () { }
};
class CwinThread : public CCmdTarget
{
DECLARE_DYNAMIC(CWinThread)
public:
CwinThread:: CwinThread () { }
CwinThread::~ CwinThread () { }
Virtual BOOL InitInstance(){
Return TRUE;
}
virtual int Run(){
return 1;
}
};
class CWnd;
class CWinApp : public CwinThread
{
DECLARE_DYNAMIC(CWinApp)
public:
CWinApp * m_pCurrentWinApp;
CWnd * m_pMainWnd;
Public:
CWinApp::CWinApp() { pCurrentWinApp = this ;}
CWinApp::~CWinApp() { }
virtual BOOl InitApplication () {
Return TRUE;
}
virtual BOOL InitInstance () {
return TRUE;
}
virtual int Run () {
return CWinThread::Run();
}
};
Class CDocument : public CCmdTarget
{
DECLARE_DYNAMIC(CDocument)
public:
Cdocument:: Cdocument() { }
Cdocument::~ Cdocument() { }
};
Class CWnd : public CCmdTarget
{
DECLARE_DYNAMIC(CWnd)
public:
CWnd:: CWnd () { }
CWnd::~CWnd () { }
};
Class CFrameWnd : public CWnd
{
DECLARE_DYNAMIC(CFrameWnd)
public:
CFrameWnd:: CFrameWnd () { }
CFrameWnd::~CFrameWnd () { }
BOOL Create ();
Virtual BOOL PreCreateWindow();
};
Class CView : public CWnd
{
DECLARE_DYNAMIC(CView)
public:
CView:: CView () { }
CView::~ CView () { }
};
//global function
CWinApp *AfxGetApp();
MFC.CPP
#include
“
my.h
”
//本该包含mfc.h,但为了CmyWinapp的定义
extern CmyWinApp theApp ; //external global object
static char szCObject[] =
“
CObject
”
;
struct CRuntimeClass CObject::classCObject =
{szCObject,sizeof( CObject),0XFFFF,NULL,NULL,NULL };
static AFX_CLASSINIT _init_CObject(&CObject::classCObject);
CRuntimeClass *CRuntimeClass::pFirstClass = NULL;
AFX_CLASSINIT::AFX_CLASSINIT(CRuntimeClass * pNewClass)
{
pNewClass->m_pNextclass=CRuntimeClass ::pFirstClass;
CRuntimeClass::pFirstClass = pNewClass;
}
CRuntimeClass *CObject::GetRuntimeClass() const
{
return &CObject::classCObject;
}
BOOL CWnd::Create()
{
return TRUE;
}
BOOL CWnd::CreateEx()
{
PreCreateWindow();
return TRUE;
}
BOOL CWnd::PreCreateWindow()
{
return TRUE;
}
BOOL CframeWnd::Create()
{
CreateEx();
return TRUE;
}
BOOL CframeWnd::PreCreateWindow()
{
return TRUE;
}
INPLEMENT_DYNAMIC(CcmdTarget,CObject)
INPLEMENT_DYNAMIC(CWinThread, CcmdTarget)
INPLEMENT_DYNAMIC(CWinAPP, CWinThread)
INPLEMENT_DYNAMIC(CWnd,CCmdTarget)
INPLEMENT_DYNAMIC(CFrameWnd, CWnd)
INPLEMENT_DYNAMIC(CDocument, CcmdTarget)
INPLEMENT_DYNAMIC(CView,CWnd)
//global function
CWinApp * AfxApp()
{
return theApp.m_pCurrentWinApp;
}
MY.H
#include <iostream.h>
#include
“
mfc.h
”
class CmyWinApp : public CWinApp
{
public:
CmyWinApp:: CmyWinApp() { }
CmyWinApp::~ CmyWinApp() { }
virtual BOOL InitInstance();
};
class CMyFrameWnd : public CframeWnd
{
public:
CMyFrameWnd();
~CMyFrameWnd() {}
};
class CmyDoc : public Cdocument
{
public:
CmyDoc:: CmyDoc() { }
CmyDoc::~ CmyDoc() { }
};
class CMyView : public CView
{
public:
CMyView:: CMyView () { }
CMyView::~ CMyView () { }
};
//global function
coid PrintAllClasses()
MFC.CPP
#include
“
my.h
”
CmyWinApp theApp; //global object
BOOL CMyWinAPP::InitInstance()
{
m_pMainWnd = new CmyFrameWnd;
return TRUE;
}
CmyFrameWnd::CmyFrameWnd()
{
Create();
}
void PrintAllClasses()
{
CRuntimeClass *pClass;
//just walk through the simple list of regidtered classes
for(pClass = CRuntimeClass::pFirstClass;pClass!=NULL;
pClass = pCLass->m_pNextClass)
{
cout<<pClass->m_lpszClassName<<
”
/n
”
;
cout<<pClass->m_nObjectSize<<
”
/n
”
;
cout<<pClass->m_wSchema<<
”
/n
”
;
}
}
//main()
void main()
{
CwinApp *pApp = AfxGetApp();
pApp->InitApplication();
pApp->InitInstance();
pApp->Run();
PrintAllClasses();
}
执行结果:
Cview
4
66535
Cdocument
4
66535
CframeWnd
4
66535
CWnd
4
66535
CwinApp
12
66535
CwinThread
4
66535
CcmdTarget
4
66535
CObject
4
66535
IsKindOf(类型识别)
1 为CObject加上一个IsKindOf函数,于是此函数将所有类继承。它将把参数所指定的某个CRuntimeClass对象拿来与类别型录中元素一一比较。成功返回TRUE,失败返回FALSE。
Class CObject
{
public:
…
BOOL IsKindOf(const CRuntimeClass *pClass) const;
};
BOOL Cobjecta;;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。
2 IsKindOf的使用方式如下:
CMyDoc *pMyDoc = new CMyDoc;
CMyView *pMyView = new CMyView;
Cout<<pMyDoc->IsKindOf(RUNTIME_CLASS(CMyDoc)); //TRUE
Cout<<pMyView->IsKindOf(RUNTIME_CLASS(CView));//TRUE