第三章:MFC六大關鍵技術之仿真:類型識別
深入理解MFC的內部運行原理,是本次學習《深入淺出MFC》的主要目的。要模仿的六大技術包括:
1:MFC程序的初始化過程。
2:RTTI(Runtime type identification)運行時類型識別。
3:Dynamic creation 動態創建
4:Persistence永久保存
5:消息映射
6:消息傳遞。
RTTI(運行時類型識別)
IsKindOf能夠偵測某個對象是否屬於某種類。即判斷某一對象所屬的類是否是父類或當前類;
要達到動態類型識別的能力,必須在構建類繼承體系時記錄必要的信息,這被稱爲類型型錄表。MFC以鏈表的方式建立了此表。
類型型錄表的每個元素爲CRuntimeClass類型,其定義爲:
- class CRuntimeClass
- {
- public:
- LPCSTR m_lpszClassName;//對象所屬類名
- Int m_nObjectSize;//對象大小
- UINT m_wSchema;//模式號
- CObject *(PASCAL*m_pfnCreateObject)();//構建函數抽象類爲NULL
- CRuntimeClass *pBaseClasss;//基類CRuntimeClass對象指針。
- Static CRuntimeClass *pFirstClass;//鏈表頭指針。
- CRuntimeClass *m_pNextClass;//下一指針。
- };
MFC使用此類作爲每個類的成員變量。使用宏定義爲每個類定義了自己的CRuntimeClass成員變量。
DECLAR_DYNAMIC和IMPLENMENT_DYNAMIC宏
使用這兩個宏將CRuntimeClass對象不知不覺放到類之中。
DECLARE_DYNMIC宏定義如下:
- #define DELCARE_DYNMIC ( class_name ) \
- public:\
- static CRuntimeClass class##class_name \
- virtual CRuntimeClass *GetRuntimeClass()const;
##用來告訴編譯器把兩個字符串連接起來。
如果使用這個宏:DELCARE_DYNMIC(CView);
那麼預編譯器將生成下列代碼:
- public:
- static CRuntimeClass classCView;
- virtual CRuntimeClass*GetRuntimeClass()const;
以上代碼僅僅是在類中定義CRuntimeClass對象,並定義一個返回CRuntimeClass對象地址的函數。注意CRuntimeClass是static的,也就是說同一種類繼承體系的對象共享一個CRuntimeClass對象。
初始化對象的內容以及建立類型型錄表需要使用IMPLEMENT_DYNMIC宏。
- #define IMPLEMENT_DYNMIC (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##classname;\
- }
- #define RUNTIME_CLASS(class_name)\
- ( &class_name::class##class_name);
AFX_CLASSINIT是一個類,看着跟宏定義似的,這樣做很容易讓人迷惑。它用於將本節點連接到類型型錄表,定義如下:
- class AFX_CLASSINIT
- {
- public:
- AFX_CLASSINIT(CRuntimeClass*pNewClass)//構造函數
- {
- pNewClass->m_pNextClass=CRuntime::pFirstClass;
- CRuntimeClass::pFirstClass =pNewClass;
- }
- };
用法:
- class CWnd:public CCmdTarget
- {
- public:
- DECLARE_DYNAMIC(CWnd);
- };
IMPLEMENT_DYNMIC(CWnd,CCmdTarget);
代碼展開後爲;
- class CWnd:public CCmdTarget
- {
- public:
- static CRuntimeClass classCView;
- virtual CRuntimeClass*GetRuntimeClass()const
- };
- static char _lpszCWnd[]="CWnd";
- CRuntimeClass CWnd::classCWnd=
- {
- _lpszCView , sizeof(CWnd) , FFFF,NULL , &Wnd::classCWnd , NULL);
- };
- static AFX_CLASSINIT _init_CWnd(&CWnd::classCWnd);
- {
- Return &CWnd::classCWnd;
- }
定義宏的過程很複雜,但是一旦定義好之後,在使用時僅僅兩句話就可以完成定義CRuntimeClass對象並且連接類型型錄鏈表的工作。
CObject是所有類的基類,也是鏈表的頭,此類應特別定義,不能在CObject內使用定義好的宏。
- class CObject
- {
- public:
- virtual CRuntimeClass*GetRuntimeClass()const;
- static CRuntimeClass classCObject;
- };
- static char szCobject[]="CObject";
- struct CRuntimeClass CObject::classCObject=
- {
- szCObject ,sizeof(CObject),0xFFFF,NULL,NULL,NULL
- };
- static AFX_CLASSINIT _init_CObject(&Cobject::classObject);
- CRuntimeClass *CObject::GetRuntimeClass()const
- {
- return &CObject::classCObject;
- }
由於CRuntimeClass對象是static成員變量,因此需要在類外初始化。如果忘記初始化將會報鏈接錯誤。
CRuntimeClass*CRuntimeClass::pFirstClass=NULL;
建好了類類型路表,要實現IsKindOf功能很容易。首先在CObject加上一個IsKindOf函數,於是所有繼承自此類的類都具有類型識別的功能。能夠將某個CRuntimeClass對象與類類型型錄中的元素進行比較。如:
- class CObject
- {
- public:
- bool IsKindOf(const CRuntimeClass*pClass)const
- {
- CRuntimeClass *pClassThis=GetRuntimeClass();
- while(pClassThis)
- {
- if(pClassThis==pClass)
- return true;
- pClassThis=pClassThis->m_pBaseClass;//沿着基類尋找。
- }
- return false;
- }
- };
如果我們調用CWnd *cw=new CWnd;
cw->IsKindOf(RUNTIME_CLASS(CFrameWnd));
RUNTIME_CLASS實際就是&CFrameWnd::classCFrameWnd,它就是CFrameWnd的static的CRuntimeClass類型成員。函數內利用GetRuntimeClass取得本類的CRuntimeClass對象的地址,即&CWnd::classCWnd,然後進行比較。因爲每一類型共用一個static的CRuntimeClass對象,因此屬於同於類的CRuntimeClass對象的地址相同。
動態創建
每一類的構建函數可以記錄在類型別錄中,當獲得一個類名稱,通過查找類別型錄表找出對應的元素,然後調用其構建函數產生新對象。
在CRuntimeClass中m_pfnCreateObject即爲構建函數首地址。
爲了實現動態創建,需要添加兩個宏:
DECLARE_DYNCREATE和IMPLEMENT_DYNCREATE。
如:
- #define DECLARE_DYNCREATE(class_name)\
- DECLARE_DYNCREATE(class_name)\
- static CObject *PASCAL CreateObject();
- #define IMPLEMENT_DYNCREATE (class_name,base_class_name)\
- CObject*PASCAL class_name::CreateObject()\
- {return new classname;};\
- _IMPLEMENT_RUNTIMECLASS(class_name,base_class_name,
- 0xFFFF,class_name::CreateObject)
以CFrameWnd爲例,下列程序代碼:
- class CFrameWnd:public CWnd
- {
- public:
- DECLEARE_DYNCREATE(CFrameWnd);
- };
IMPLEMENT_DYNCREATE(CFrameWnd,CWnd);
展開如下:
- class CFrame:public CWnd
- {
- public:
- static CRuntimeClass classCFrameWnd;
- virtual CRuntimeClass *GetRuntimeClass()const;
- static CObject *PASCAL CreateObject();
- };
- CObject _PASCAL CFrameWnd::CreateObject()
- {
- return new CFrameWnd;
- }
- static char _lpszCFrameWnd[]="CFrameWnd";
- CRuntimeClass CFrameClass::classCFrameWnd={
- _lpszCFrameWnd,sizeof(CFrameWnd),0xFFFF,CFrameWnd::CreateObject,RUNTIME_CALSS(CWnd),NULL};
- static AFX_CLASSINIT _init_CFrameWnd
- (&CFrameWnd::classCFrameWnd);
- CRuntimeClass*CFrameWnd::GetRunimeClass()const
- {return &CFrameWnd::classCFrameWnd;}
注意對象構建函數爲static函數。
爲了支持動態創建需要在CRuntimeClass內添加兩個函數:CreateObject和CRuntimeClass::Load成員函數。
- CObject *CRuntimeClass::CreateObject()
- {
- If(m_pfnCreateObject==NULL)//不支持動態創建。
- {
- throw runtime_error("此類不支持動態創建");
- Return NULL;
- }
- CObject*pObject=(*m_pfnCreateObject)();
- Return pObject;
- }
- CRuntimeClass*PASCL CRuntimeClass::Load()
- {
- Char szClassName[64];
- CRuntimeClass*pClass
- cout<<"輸入一個類名:";
- cin>>szClassName;
- for(pClass=pFirstClass;pClass;pClass=pClass->m_pNextClass)
- {
- if(strcmp(szClassName,pClass->m_lpszClassName)==0)
- return pClass;
- return NULL;
- }
- }
以下爲類型識別及動態創建的完整代碼:
- <span style="font-size:18px;">#include<iostream>
- #include<windows.h>
- #include<string>
- using namespace std;
- class CObject;
- class CRuntimeClass
- {
- public:
- char* m_lpszClassName;//對象所屬類名
- int m_nObjectSize;//對象大小
- int m_wSchema;//模式號
- CObject*(PASCAL*m_pfnCreateObject)();//構建函數,抽象類爲NULL
- CRuntimeClass *m_pBaseClasss;//基類CRuntimeClass對象指針。
- static CRuntimeClass *pFirstClass;//鏈表頭指針。static
- CRuntimeClass *m_pNextClass;//下一指針。
- public:
- CObject*CreateObject()
- {
- if(m_pfnCreateObject==NULL)
- {
- cout<<"該類型不支持動態創建!!"<<endl;
- return NULL;
- }
- CObject*pClass=(*m_pfnCreateObject)();
- return pClass;
- }
- static CRuntimeClass*Load()
- {
- cout<<"請輸入要動態創建的類名:";
- string s;
- cin>>s;
- for(CRuntimeClass*pClass=pFirstClass;pClass;pClass=pClass->m_pBaseClasss)
- {
- if(pClass->m_lpszClassName==s)
- {
- return pClass;
- }
- }
- return NULL;
- }
- };
- class AFX_CLASSINIT
- {
- public:
- AFX_CLASSINIT(CRuntimeClass*pNewClass)//構造函數
- {
- pNewClass->m_pNextClass=CRuntimeClass::pFirstClass;
- CRuntimeClass::pFirstClass =pNewClass;
- }
- };
- /************************************************************************/
- /* 動態類型識別宏定義
- //與CRuntimeClass類中的構建函數相區別。此處的CreateObject函數在每個類中都以static成員函數存在,用以
- //初始化類型型錄表,而CRuntimeClass中的CreateObject用於調用每個類的構建函數。僅僅是函數名相同罷了。*/
- /************************************************************************/
- #define DECLARE_DYNAMIC(class_name)\
- public:\
- static CRuntimeClass Class##class_name;\
- virtual CRuntimeClass*GetRuntimeClass()const;\
- #define DECLARE_DYNCREATE(class_name)\
- DECLARE_DYNAMIC(class_name)\
- static CObject*PASCAL CreateObject();\
- #define RUNTIME_CLASS(class_name)\
- (&class_name::Class##class_name)\
- #define _IMPLEMENT_RUNTIMECLASS(class_name,base_class_name,wSchema,pfnNew)\
- class CRuntimeClass class_name::Class##class_name ={\
- #class_name,\
- sizeof(class_name),wSchema,pfnNew,RUNTIME_CLASS(base_class_name),NULL};\
- static AFX_CLASSINIT _init##class_name( RUNTIME_CLASS(class_name));\
- CRuntimeClass *class_name::GetRuntimeClass()const\
- {return &class_name::Class##class_name;}//此處將class_name寫成了classname花了一兩天才查出來。啊啊啊啊啊。20120605
- #define IMPLEMENT_DYNAMIC(class_name,base_class_name)\
- _IMPLEMENT_RUNTIMECLASS(class_name,base_class_name,0xFFFF,NULL)\
- #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)\
- /************************************************************************/
- /* 對CObject特殊處理。 */
- /************************************************************************/
- class CObject
- {
- public:
- CObject()
- {
- //cout<<"CObject constructor!"<<endl;
- }
- ~CObject()
- {
- //cout<<"CObject destructor!"<<endl;
- }
- public:
- virtual CRuntimeClass*GetRuntimeClass();
- static CRuntimeClass ClassCObject;
- public:
- bool IsKindOf(CRuntimeClass*pClass)
- {
- CRuntimeClass *pThis=GetRuntimeClass();
- for(;pThis;pThis=pThis->m_pBaseClasss)
- {
- if(pThis==pClass)
- {
- return true;
- }
- }
- return false;
- }
- };
- class CRuntimeClass CObject:: ClassCObject=
- {
- "CObject",sizeof(CObject),0xFFFF,NULL,NULL,NULL
- };
- static AFX_CLASSINIT _init_CObject(&CObject:: ClassCObject);
- CRuntimeClass *CObject::GetRuntimeClass()
- {
- return &CObject::ClassCObject;
- }
- CRuntimeClass*CRuntimeClass::pFirstClass=NULL;
- /************************************************************************/
- /* */
- /************************************************************************/
- class CCmdTarget:public CObject
- {
- DECLARE_DYNCREATE(CCmdTarget)
- public:
- CCmdTarget()
- {
- //cout<<"CCmdTarget constructor!"<<endl;
- //CreateObject();
- }
- ~CCmdTarget()
- {
- //cout<<"CCmdTarget destructor!"<<endl;
- }
- };
- IMPLEMENT_DYNCREATE(CCmdTarget,CObject)
- ;
- class CWnd:public CCmdTarget
- {
- DECLARE_DYNCREATE(CWnd)
- public:
- CWnd()
- {
- //cout<<"CWnd constructor"<<endl;
- }
- ~CWnd()
- {
- //cout<<"CWnd destructor"<<endl;
- }
- public:
- virtual bool Create()
- {
- cout<<"CWnd::Create"<<endl;
- CreateEx();
- return true;
- }
- bool CreateEx()
- {
- cout<<"CWnd::CreateEx"<<endl;
- PreCreateWindow();
- return true;
- }
- virtual bool PreCreateWindow()
- {
- cout<<"CWnd::PreCreateWindow"<<endl;
- return true;
- }
- };
- IMPLEMENT_DYNCREATE(CWnd,CCmdTarget)
- class CView :public CWnd
- {
- DECLARE_DYNCREATE(CView)
- public:
- CView()
- {
- //cout<<"CView constructor"<<endl;
- }
- ~CView()
- {
- //cout<<"CView destructor"<<endl;
- }
- };
- IMPLEMENT_DYNCREATE(CView,CWnd)
- class CFrameWnd:public CWnd
- {
- DECLARE_DYNCREATE(CFrameWnd)
- public:
- CFrameWnd()
- {
- //cout<<"CFrameWnd constructor"<<endl;
- }
- ~CFrameWnd()
- {
- //cout<<"CFrameWnd destructor"<<endl;
- }
- public:
- virtual bool Create()
- {
- cout<<"CFrameWnd::Create"<<endl;
- CreateEx();
- return true;
- }
- virtual bool PreCreateWindow()
- {
- cout<<"CFrameWnd::PreCreateWindow"<<endl;
- return true;
- }
- };
- IMPLEMENT_DYNCREATE(CFrameWnd,CWnd)
- class CWinThread:public CCmdTarget
- {
- public:
- CWinThread()
- {
- //cout<<"CWinThread constructor"<<endl;
- }
- ~CWinThread()
- {
- //cout<<"CWinThread destructor"<<endl;
- }
- public:
- virtual bool InitInstance()
- {
- cout<<"CWinThread::InitInstance"<<endl;
- return true;
- }
- virtual bool Run()
- {
- cout<<"CWinThread::Run"<<endl;
- return true;
- }
- };
- class CWinApp:public CWinThread
- {
- public:
- CWinApp()
- {
- //cout<<"CWinApp Constructor "<<endl;
- m_currentApp=this;
- }
- ~CWinApp()
- {
- //cout<<"CWinApp destructor "<<endl;
- }
- virtual bool InitApplication()
- {
- cout<<"CWinApp::InitApplication"<<endl;
- return true;
- }
- virtual bool InitInstance()
- {
- cout<<"CWinApp:InitInstance"<<endl;
- return true;
- }
- virtual bool Run()
- {
- cout<<"CWinApp::Run"<<endl;
- return CWinThread::Run();
- }
- public:
- CWinApp*m_currentApp;
- CFrameWnd*m_currentFrameWnd;
- };
- class CDocument:public CCmdTarget
- {
- public:
- CDocument()
- {
- //cout<<"CDocument constructor "<<endl;
- }
- ~CDocument()
- {
- //cout<<"CDocunment destructor "<<endl;
- }
- };
- class CMyFrameWnd:public CFrameWnd
- {
- DECLARE_DYNCREATE(CMyFrameWnd)
- public:
- CMyFrameWnd()
- {
- //cout<<"CMyFrameWnd constructor "<<endl;
- Create();
- }
- ~CMyFrameWnd()
- {
- //cout<<"CMyFrameWnd destructor "<<endl;
- }
- };
- IMPLEMENT_DYNCREATE(CMyFrameWnd,CFrameWnd)
- class CMyWinApp:public CWinApp
- {
- public:
- CMyWinApp()
- {
- //cout<<"CMyWinApp constructor "<<endl;
- }
- ~CMyWinApp()
- {
- //cout<<"CMyWinApp destructor "<<endl;
- }
- public:
- bool InitInstance()
- {
- cout<<"CMyWinApp::InitInstance"<<endl;
- m_currentFrameWnd=new CMyFrameWnd;
- return true;
- }
- };
- CMyWinApp myApp;
- CWinApp*AfxGetApp()
- {
- return myApp.m_currentApp;
- }
- int main(int argc,char**argv)
- {
- CWinApp *pApp=AfxGetApp();
- pApp->InitApplication();
- pApp->InitInstance();
- pApp->Run();
- CRuntimeClass *pClass;
- CObject *pOb;
- cout<<"以下爲類型型錄鏈表中的所有類的名稱:"<<endl;
- for(pClass=CRuntimeClass::pFirstClass;pClass;pClass=pClass->m_pBaseClasss)
- {
- cout<<pClass->m_lpszClassName<<endl;
- }
- while(1)
- {
- pClass=CRuntimeClass::Load();
- if(!pClass)
- {
- cout<<"找不到此類!!!"<<endl;
- }
- else
- {
- pOb=pClass->CreateObject();
- if(pOb)
- {
- cout<<"創建成功!"<<endl;
- }
- }
- }
- return 0;
- }</span>