MFC深入浅出之RTTI

来源:互联网 发布:excel表数据分类汇总 编辑:程序博客网 时间:2024/06/06 08:36
 
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) /
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) /
        (&class_name::class##class_name)
 IMPLEMENT_DYNAMIC的美妙之处在于它所使用的一个struct AFX_CLASSINIT,定义如下:
Struct 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;
   
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