【轉載之MFC】MFC程序框架分析
From: CSDN qzmguy的專欄
主要包括兩個
方面
一、程序的初始化
二、消息映射機制
以單文檔程序Test為例。程序自動生成的類為CAboutDlg、CMainFrame、CTestApp、CTestDoc、CTestView。 還有一個全局的應用程序類對象CTestApp theApp。
流程如下:
1、CTestApp theApp; //初始化全局對象
2、因為Class CTestApp:public CWinApp,所以進 入CWinApp::CWinApp(LPCTSTR lpszAppName)<位于AppCore.cpp中>
3、CTestApp::CTestApp() //調用自己的構造函數
4、進入WinMain()函數,位于AppMODUL.cpp中
//在TCHAR.h中 這么一行定義: #define _tWinMain WinMain _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { // call shared/exported WinMain return AfxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow); } |
5、進入AfxWinMain()函數中,這是MFC框 架函數,位于WinMain.cpp中
int AFXAPI AfxWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { ASSERT(hPrevInstance == NULL);
int nReturnCode = -1; CWinThread* pThread = AfxGetThread(); CWinApp* pApp = AfxGetApp();
// AFX internal initialization if (!AfxWinInit(hInstance, hPrevInstance, lpCmdLine, nCmdShow)) goto InitFailure;
// App global initializations (rare) if (pApp != NULL && !pApp->InitApplication()) goto InitFailure;
// Perform specific initializations多態性原理,實際上調用的是//CTestApp::InitInstance() ① if (!pThread->InitInstance()) { if (pThread->m_pMainWnd != NULL) { TRACE0("Warning: Destroying non-NULL m_pMainWnd\n"); pThread->m_pMainWnd->DestroyWindow(); } nReturnCode = pThread->ExitInstance(); goto InitFailure; } //CWinThread::Run()位 于THRDCORE.cpp中,由此進入消息循環 ② nReturnCode = pThread->Run();
InitFailure: #ifdef _DEBUG // Check for missing AfxLockTempMap calls if (AfxGetModuleThreadState()->m_nTempMapLock != 0) { TRACE1("Warning: Temp map lock count non-zero (%ld).\n", AfxGetModuleThreadState()->m_nTempMapLock); } AfxLockTempMaps(); AfxUnlockTempMaps(-1); #endif
AfxWinTerm(); return nReturnCode; } |
5-① |
BOOL CTestApp::InitInstance()
{ AfxEnableControlContainer(); //
Standard initialization // If
you are not using these features and wish to reduce the size // of
your final executable, you should remove from the following // the
specific initialization routines you do not need. #ifdef _AFXDLL Enable3dControls();
// Call this when using MFC in a shared DLL #else Enable3dControlsStatic();
// Call this when linking to MFC statically #endif //
Change the registry key under which our settings are stored. //
TODO: You should modify this string to be something appropriate //
such as the name of your company or organization. SetRegistryKey(_T("Local
AppWizard-Generated Applications")); LoadStdProfileSettings();
// Load standard INI file options (including MRU) //
Register the application's document templates. Document templates // serve
as the connection between documents, frame windows and views. CSingleDocTemplate*
pDocTemplate; pDocTemplate
= new CSingleDocTemplate( IDR_MAINFRAME, RUNTIME_CLASS(CTestDoc), RUNTIME_CLASS(CMainFrame), //
main SDI frame window RUNTIME_CLASS(CTestView)); AddDocTemplate(pDocTemplate); //
Parse command line for standard shell commands, DDE, file open CCommandLineInfo
cmdInfo; ParseCommandLine(cmdInfo); //
Dispatch commands specified on the command line進入注冊和創建窗口 //位于AppUI2.cpp中 ③ if
(!ProcessShellCommand(cmdInfo)) return
FALSE; //
The one and only window has been initialized, so show and update it. //顯
示和更新窗口 m_pMainWnd->ShowWindow(SW_SHOW); m_pMainWnd->UpdateWindow(); return
TRUE; } |
5-①-③ |
從③進入,再經CMainFrame::LoadFrame()進 入注冊和創建窗口 (1)注 冊窗口:AfxEndDeferRegisterClass() BOOL AFXAPI AfxEndDeferRegisterClass(LONG fToRegister) { // mask off all classes that are already registered AFX_MODULE_STATE* pModuleState = AfxGetModuleState(); fToRegister &= ~pModuleState->m_fRegisteredClasses; if (fToRegister == 0) return TRUE;
LONG fRegisteredClasses = 0;
// common initialization WNDCLASS wndcls; memset(&wndcls, 0, sizeof(WNDCLASS)); // start with NULL defaults wndcls.lpfnWndProc = DefWindowProc; wndcls.hInstance = AfxGetInstanceHandle(); wndcls.hCursor = afxData.hcurArrow;
INITCOMMONCONTROLSEX init; init.dwSize = sizeof(init);
// work to register classes as specified by fToRegister, populate fRegisteredClasses as we go …………………………………………………… if (fToRegister & AFX_WNDFRAMEORVIEW_REG) { // SDI Frame or MDI Child windows or views - normal colors wndcls.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW; wndcls.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1); if (_AfxRegisterWithIcon(&wndcls, _afxWndFrameOrView, AFX_IDI_STD_FRAME)) fRegisteredClasses |= AFX_WNDFRAMEORVIEW_REG; } ………………………………………………………… return (fToRegister & fRegisteredClasses) == fToRegister; } |
AFX_STATIC BOOL AFXAPI _AfxRegisterWithIcon(WNDCLASS* pWndCls, LPCTSTR lpszClassName, UINT nIDIcon) { pWndCls->lpszClassName = lpszClassName; HINSTANCE hInst = AfxFindResourceHandle( MAKEINTRESOURCE(nIDIcon), RT_GROUP_ICON); if ((pWndCls->hIcon = ::LoadIcon(hInst, MAKEINTRESOURCE(nIDIcon))) == NULL) { // use default icon pWndCls->hIcon = ::LoadIcon(NULL, IDI_APPLICATION); } return AfxRegisterClass(pWndCls); } |
(2)創建窗口CMainFrame::Create(), 之后再調用CreateEx() BOOL CWnd::CreateEx(DWORD dwExStyle, LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD dwStyle, int x, int y, int nWidth, int nHeight, HWND hWndParent, HMENU nIDorHMenu, LPVOID lpParam) { ……………………………………………………………… AfxHookWindowCreate(this); HWND hWnd = ::CreateWindowEx(cs.dwExStyle, cs.lpszClass, cs.lpszName, cs.style, cs.x, cs.y, cs.cx, cs.cy, cs.hwndParent, cs.hMenu, cs.hInstance, cs.lpCreateParams); if (!AfxUnhookWindowCreate()) PostNcDestroy(); // cleanup if CreateWindowEx fails too soon if (hWnd == NULL) return FALSE; ASSERT(hWnd == m_hWnd); // should have been set in send msg hook return TRUE; } 從上面的代碼看不出任何顯式的置換DefWindowProc的 代碼,其實,它隱藏在AfxHookWindowCreate(this)之 中,順藤摸瓜,再看看AfxHookWindowCreate()的 代碼。CreateEx()在調用CreateWindowEx()創 建真正的窗口對象之前,設置一個線程級的CBT Hook,該hook在 窗口創建完成后被調用,MFC在hook函 數中調用SetWindowLong()將 該窗口的窗口函數置換成AfxWndProc。 |
6、最后再經5-①-②nReturnCode = pThread->Run()進入消息循環
======================================================================
消息循環機制分析
從nReturnCode = pThread->Run()進入。CWinThread::Run()位 于THRDCORE.cpp中
int CWinThread::Run() { ASSERT_VALID(this); // for tracking the idle time state BOOL bIdle = TRUE; LONG lIdleCount = 0; // acquire and dispatch messages until a WM_QUIT message is received. for (;;) { // phase1: check to see if we can do idle work while (bIdle && !::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE)) { // call OnIdle while in bIdle state if (!OnIdle(lIdleCount++)) bIdle = FALSE; // assume "no idle" state } // phase2: pump messages while available do { // pump message, but quit on WM_QUIT if (!PumpMessage()) return ExitInstance(); // reset "no idle" state after pumping "normal" message if (IsIdleMessage(&m_msgCur)) { bIdle = TRUE; lIdleCount = 0; } } while (::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE)); } ASSERT(FALSE); // not reachable } |
BOOL CWinThread::PumpMessage() { ASSERT_VALID(this); if (!::GetMessage(&m_msgCur, NULL, NULL, NULL)) { #ifdef _DEBUG if (afxTraceFlags & traceAppMsg) TRACE0("CWinThread::PumpMessage - Received WM_QUIT.\n"); m_nDisablePumpCount++; // application must die // Note: prevents calling message loop things in 'ExitInstance' // will never be decremented #endif return FALSE; } #ifdef _DEBUG if (m_nDisablePumpCount != 0) { TRACE0("Error: CWinThread::PumpMessage called when not permitted.\n"); ASSERT(FALSE); } #endif #ifdef _DEBUG if (afxTraceFlags & traceAppMsg) _AfxTraceMsg(_T("PumpMessage"), &m_msgCur); #endif // process this message if (m_msgCur.message != WM_KICKIDLE && !PreTranslateMessage(&m_msgCur)) { //發 送消息到窗口,交由窗口過程函數處理 ::TranslateMessage(&m_msgCur); ::DispatchMessage(&m_msgCur); } return TRUE; } |
所有的窗口都只有一個相同的窗口過程窗口函數AfxWndProc,消 息傳遞圖如下所示:
AfxCallWndProc |
WindowProc |
OnWndMsg |
DefWindowProc |
OnWndMsg調用不成功,調用默認窗口過程函數 |
OnCommand |
命令消息 |
OnNotify |
通告消息 |
OnCmdMsg |
BOOL CFrameWnd:: OnCmdMsg(…) { CView pView = GetActiveView();//得 到活動視指針。 if(pView-> OnCmdMsg(…)) return TRUE; //如果CView類 對象或其派生類對象已經處理該消息,則返回。 ……//否則,同 理向下執行,交給文檔、框架、及應用程序執行自身的OnCmdMsg。 }//定義了消息在各個類中的傳遞順序
|
AfxWndProc |
消息分發中心 |
CWinThread::Run |
PumpMessage |
TranslateMessage DispatchMessage |
進入消息循環 |
投遞消息到相應窗口 |
LRESULT CALLBACK AfxWndProc(HWND hWnd,UINT nMsg,WPARAM wParam, LPARAM lParam) { …… CWnd*pWnd=CWnd::FromHandlePermanent(hWnd); //把對句柄的操作轉換成對CWnd對 象。 ReturnAfxCallWndProc(pWnd,hWnd,nMsg,wParam,lParam); } |
有關消息的幾個宏
DECLARE_MESSAGE_MAP()宏
BEGIN_MESSAGE_MAP(theClass, baseClass)和END_MESSAGE_MAP()宏
弄懂MFC消息映射機制的最好辦法是將找出一個具體的實例,將這些宏展開,并找
出相關的數據結構。
DECLARE_MESSAGE_MAP()
DECLARE_MESSAGE_MAP()宏的定義如下:
#define DECLARE_MESSAGE_MAP() \
private: \
static const AFX_MSGMAP_ENTRY _messageEntries[]; \
protected: \
static AFX_DATA const AFX_MSGMAP messageMap; \
virtual const AFX_MSGMAP* GetMessageMap() const; \
從上面的定義可以看出,DECLARE_MESSAGE_MAP()作下面三件 事:
定義一個長度不定的靜態數組變量_messageEntries[];
定義一個靜態變量messageMap;
定義一個虛擬函數GetMessageMap();
在DECLARE_MESSAGE_MAP()宏中,涉及到MFC中 兩個對外不公開的數據結構
AFX_MSGMAP_ENTRY和AFX_MSGMAP。 為了弄清楚消息映射,有必要考察一下這兩個數據結構的定義。
AFX_MSGMAP_ENTRY的定義
struct AFX_MSGMAP_ENTRY
{
UINT nMessage; // windows message
UINT nCode; // control code or WM_NOTIFY code
UINT nID; // control ID (or 0 for windows messages)
UINT nLastID; // used for entries specifying a range of control id's
UINT nSig; // signature type (action) or pointer to message #
AFX_PMSG pfn; // routine to call (or special value)
};
結構中各項的含義注釋已經說明得很清楚了,這里不再多述,從上面的定義你是否看出,AFX_MSGMAP_ENTRY結 構實際上定義了消息和處理此消息的動作之間的映射關系。因此靜態數組變量_messageEntries[]實 際上定義了一張表,表中的每一項指定了相應的對象所要處理的消息和處理此消息的函數的對應關系,因而這張表也稱為消息映射表。再看看AFX_MSGMAP的 定義。
(2)AFX_MSGMAP的 定義
struct AFX_MSGMAP
{
const AFX_MSGMAP* pBaseMap;
const AFX_MSGMAP_ENTRY* lpEntries;
};
不難看出,AFX_MSGMAP定義了一單向鏈表, 鏈表中每一項的值是一指向消息映射表的指針(實際上就是_messageEntries的 值)。通過這個鏈表,使得在某個類中調用基類的的消息處理函數很容易,因此,“父類的消息處理函數是子類的缺省消息處理函數” 就“順理成章”了。在后面的“MFC窗口的消息處理”一節中會對此作詳細的講解。
BEGIN_MESSAGE_MAP()和END_MESSAGE_MAP()
它們的定義如下:
#define BEGIN_MESSAGE_MAP(theClass, baseClass) \
const AFX_MSGMAP* theClass::GetMessageMap() const \
{ return &theClass::messageMap; } \
AFX_COMDAT AFX_DATADEF const AFX_MSGMAP theClass::messageMap = \
{ &baseClass::messageMap, &theClass::_messageEntries[0] }; \
AFX_COMDAT const AFX_MSGMAP_ENTRY theClass::_messageEntries[] = \
{ \
#define END_MESSAGE_MAP() \
{0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 } \
}; \
對應BEGIN_MESSAGE_MAP()的定義可能不是一下子就看得明白, 不過不要緊,舉
一例子就很清楚了。對于BEGIN_MESSAGE_MAP(CView, CWnd),VC預編譯器將其展開成下面 的形式:
const AFX_MSGMAP* CView::GetMessageMap() const
{
return &CView::messageMap;
}
AFX_COMDAT AFX_DATADEF const AFX_MSGMAP CView::messageMap =
{
&CWnd::messageMap,
&CView::_messageEntries[0]
};
AFX_COMDAT const AFX_MSGMAP_ENTRY CView::_messageEntries[] =
{
至于END_MESSAGE_MAP()則不過定義了一個表示映射表 結束的標志項,我想大家對于這種簡單的技巧應該是很熟悉的,無需多述。
到此為止,我想大家也已經想到了象ON_COMMAND這 樣的宏的具體作用了,不錯它們只不過定義了一種類型的消息映射項,看看ON_COMMAND的定 義:
#define ON_COMMAND(id, memberFxn) \
{ WM_COMMAND, CN_COMMAND, (WORD)id, (WORD)id, AfxSig_vv, (AFX_PMSG)&memberFxn },
根據上面的定義,ON_COMMAND(ID_FILE_NEW, OnFileNew)將 被VC預編譯器展開
如下:
{WM_COMMAND, CN_COMMAND, (WORD)id, (WORD)id, AfxSig_vv,
(AFX_PMSG)&OnFileNew},
到此,MFC的消息映射機制已經清楚了,現在提出并解答兩個問題以作為對這一節 的小結。
為什么不直接使用虛擬函數實現消息處理函數呢?這是一個GOOD QUESTION。 前面已經說過,MFC的設計者們在設計MFC時 有一個很明確的目標,就是使得“MFC的代碼盡可能小,速度盡可能快”,如果采用虛擬函數,那么對 于所有的窗口消息,都必須有一個與之對應的虛擬函數,因而對每一個從CWnd派 生的類而言,都會有一張很大的虛擬函數表vtbl。但是在實際應用中, 一般只對少數的消息進行處理,大部分都交給系統缺省處理,所以表中的大部分項都是無用項,這樣做就浪費了很多內存資源,這同MFC設 計者們的設計目標是相違背的。當然,MFC所使用的方法只是解決這類問題的方式之一,不排除還有其 他的解決方式,但就我個人觀點而言,這是一種最好的解決方式,體現了很高的技巧性,值得我們學習。
至于這第二個問題,是由上面的問題引申出來的。如果在子類和父類中出現了相同的消息出來函數,VC編 譯器會怎么處理這個問題呢?VC不會將它們看作錯誤,而會象對待虛擬函 數類似的方式去處理,但對于消息處理函數(帶afx_msg前 綴),則不會生成虛擬函數表vtbl。
======================================================================
RTTI實現機制
C++設計者在C++使用的早期并沒有意識到RTTI(運 行時類型檢查)的重要性,后來
隨作框架結構的類庫出現及其應用越來越廣泛,RTTI就 變得越來越重要了。例如下面的這個語句:
CWnd *pWnd;
任何人都知道對象p是CWnd類 型的指針。但是如果有一個類CView是從CWnd派 生來的,對于下面的語句:
CWnd* CreateView()
{
return new CView;
}
對于使用CreateView()的 用戶而然,pWnd = CreateView(), 他如何確定pWnd所指向的對象的真正類型呢?因此,必須有一個能夠在 運行時刻就能夠確定指針對象類型的方法,比如給每一個類型的對象均添加一個IsKindOf()之 類的方法,通過此方法判斷指針對象的類型。
后來,RTTI被加入了C++的 規范,成為C++一個內置的特性。
在MFC的設計者們設計MFC的 時候,C++規范中并沒有包含RTTI,但 是他們很早就意識到這個問題,所以他們以一種獨特的方式在MFC中實現RTTI, 采用這種方式實現的RTTI對于某個對象而言并不是必須的,也就是說,MFC的 設計者們并不將RTTI強加于用戶所設計的類型上,而是讓用戶根據自己的需要選擇是否他所設計的類 型需要RTTI。因而這種方式比C++規范 中內置的RTTI更靈活。
MFC的設計者們在MFC中采用下面的的方 法來實現RTTI:
設計一個基類CObject, 在CObject中增加RTTI功 能,任何一個類型,如果需
要具有RTTI功能,就必須直接或間接派生于CObject
采用宏實現RTTI,對于某個直接或間接從CObject派 生來的類型而言,該宏可
有可無,如果有該宏,它就具有RTTI功能,反之則無。
<一>考察CObject
我們先從CObject開始,下面是它的定義:
class AFX_NOVTABLE CObject
{
public:
// Object model (types, destruction, allocation)
virtual CRuntimeClass* GetRuntimeClass() const;
virtual ~CObject(); // virtual destructors are necessary
// Diagnostic allocations
void* PASCAL operator new(size_t nSize);
void* PASCAL operator new(size_t, void* p);
void PASCAL operator delete(void* p);
void PASCAL operator delete(void* p, void* pPlace);
void PASCAL operator delete(void *p, LPCSTR lpszFileName, int nLine);
// Disable the copy constructor and assignment by default so you will get
// compiler errors instead of unexpected behaviour if you pass objects
// by value or assign objects.
protected:
CObject();
private:
CObject(const CObject& objectSrc); // no implementation
void operator=(const CObject& objectSrc); // no implementation
// Attributes
public:
BOOL IsSerializable() const;
BOOL IsKindOf(const CRuntimeClass* pClass) const;
// Overridables
virtual void Serialize(CArchive& ar);
// Implementation
public:
static const AFX_DATA CRuntimeClass classCObject;
};
總的來說,CObject定義了整個從其 派生的家族的所有成員所具有的兩個基本的能力:
運行時的動態類型檢查(RTTI)能力和序列化能力。在早期的C++版 本中,沒有規定RTTI,但MFC的作者們早 就未撲先知,以這種構架的形式定義并實現RTTI。體現RTTI的 是CObject中的兩個成員函數:
virtual CRuntimeClass * GetRuntimeClass() const;
BOOL IsKindOf(const CRuntimeClass *pClass) const;
其中,前一個函數用來訪問存儲RTTI信息的一個CRuntimeClass類 型的結構,后一個函數供在運行時刻進行類型判斷。我們先來看看CRuntimeClass結 構的定義,看看它究竟保存了哪些類型信息。
<<From afx.h>>
struct CRuntimeClass
{
// Attributes
LPCSTR m_lpszClassName;
int m_nObjectSize;
UINT m_wSchema; // schema number of the loaded class
CObject* (PASCAL* m_pfnCreateObject)(); // NULL => abstract class
CRuntimeClass* m_pBaseClass;
// Operations
CObject* CreateObject();
BOOL IsDerivedFrom(const CRuntimeClass* pBaseClass) const;
// Implementation
void Store(CArchive& ar) const;
static CRuntimeClass* PASCAL Load(CArchive& ar, UINT* pwSchemaNum);
// CRuntimeClass objects linked together in simple list
CRuntimeClass* m_pNextClass; // linked list of registered classes
};
上面就是CRuntimeClass的定義,m_lpszClassName保 存類的名稱,m_nObjectSize保存類的實例數據所占內存的的大 小。我們重點要關注的是m_pBaseClass成員,它是指向名稱為m_lpszClassName的 類的基類的CRuntimeClass的指 針,因此,CRuntimeClass就形成了一個繼承鏈表,這個鏈表 記錄了某一族類的繼承關系。
RTTI的實現:
實現RTTI的除了上面兩個函數外,還有幾個相關的宏。我們先看看GetRuntimeClass()和IsKindOf()的 實現.
1.GetRuntimeClass()的 實現
CRuntimeClass* CObject::GetRuntimeClass() const
{
return RUNTIME_CLASS(CObject);
}
關鍵就在RUNTIME_CLASS這個宏上,RUNTIME_CLASS宏 的實現如下:
#define RUNTIME_CLASS(class_name) ((CRuntimeClass*)(&class_name::class##class_name))
將宏展開,上面的實現就變成:
CRuntimeClass* CObject::GetRuntimeClass() const
{
return (CRuntimeClass*)(&CObject::classCObject);
}
也就是說,它返回CObject類的一個static型 的成員classCObject。
2.IsKindOf()的 實現
BOOL CObject::IsKindOf(const CRuntimeClass* pClass) const
{
ASSERT(this != NULL);
// it better be in valid memory, at least for CObject size
ASSERT(AfxIsValidAddress(this, sizeof(CObject)));
// simple SI case
CRuntimeClass* pClassThis = GetRuntimeClass();
return pClassThis->IsDerivedFrom(pClass);
}
前兩行我們不管它,關鍵在于最后一行pClassThis->IsDerivedFrom(pClass),歸 根結底就是調用CRuntimeClass的IsDerivedFrom()方 法。下面是CRuntimeClass的成員IsDerivedFrom()的 實現:
BOOL CRuntimeClass::IsDerivedFrom(const CRuntimeClass* pBaseClass) const
{
ASSERT(this != NULL);
ASSERT(AfxIsValidAddress(this, sizeof(CRuntimeClass), FALSE));
ASSERT(pBaseClass != NULL);
ASSERT(AfxIsValidAddress(pBaseClass, sizeof(CRuntimeClass), FALSE));
// simple SI case
const CRuntimeClass* pClassThis = this;
while (pClassThis != NULL)
{
if (pClassThis == pBaseClass) return TRUE;
pClassThis = pClassThis->m_pBaseClass;
}
return FALSE; // walked to the top, no match
}
關鍵是上面的一段循環代碼:
while (pClassThis != NULL)
{
if (pClassThis == pBaseClass) return TRUE;
pClassThis = pClassThis->m_pBaseClass;
}
它從繼承鏈表的某一節點this開始,向后搜索比較,確定繼承關系。
將到這里,或許有人要問,這些CRuntimeClass結 構是如何產生的呢?這是一個很好的問題,解決了這個問題,就完全清楚了MFC中RTTI的 實現。使用過Visual C++開發程序的人都應該記得DECLARE_DYNAMIC和IMPLEMENT_DYNAMIC這 兩個宏,它們分別用來定義相應類的static CRuntimeClass成 員和對該成員初始化。
DECLARE_DYNAMIC宏的定義:
#define DECLARE_DYNAMIC(class_name) \
public: \
static const AFX_DATA CRuntimeClass class##class_name; \
virtual CRuntimeClass* GetRuntimeClass() const; \
例如DECLARE_DYNAMIC(CView)展 開成為:
public:
static const AFX_DATA CRuntimeClass classCView;
virtual CRuntimeClass* GetRuntimeClass() const;
由此可見,DECLARE_DYNAMIC宏用來在類的定義中定義靜態CRuntimeClass變 量和虛擬GetRuntimeClass()函 數。可以推斷,IMPLEMENT_DYNAMIC宏一定是用來初始化該靜態變量和實現GetRuntimeClass()函 數,。不錯,正是這樣!
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) \
AFX_COMDAT const AFX_DATADEF CRuntimeClass class_name::class##class_name = { \
#class_name, sizeof(class class_name), wSchema, pfnNew, \
RUNTIME_CLASS(base_class_name), NULL }; \
CRuntimeClass* class_name::GetRuntimeClass() const \
{ return RUNTIME_CLASS(class_name); } \
例如IMPLEMENT_DYNAMIC(CView, CWnd)展開如下:
//下面展開的代碼用來初始化靜態CRuntimeClass變 量
AFX_COMDATA const AFX_DATADEF CRuntimeClass CView::classCView =
{
“CView”, //m_lpszClassName
sizeof(class CView), //m_nObjectSize
0xffff, //m_wSchema
NULL, //m_pfnCreateObject
(CRuntimeClass*)(&CWnd::classCWnd), //m_pBaseClass
NULL //m_pNextClass
}
//下面的代碼用來實現GetRuntimeClass()函 數
CRuntimeClass* CView::GetRuntimeClass() const
{ return (CRuntimeClass*)(&CView::classCView);}
總的來說,同RTTI有關的宏有下面幾對:
DECLARE_DYNAMIC和IMPLEMENT_DYNAMIC
這一對宏能夠提供運行是類型判斷能力。(定義并實現IsKindOf())
DECLARE_DYNCREATE和IMPLEMENT_DYNCREATE
這一對宏除了能夠提供類型判斷能力外,還能夠提供動態創建對象的能力。
(定義并實現IsKindOf()和CreateObject())
DECLARE_SERIAL和IMPLEMENT_SERIAL
這一對宏除了提供類型判斷能力、動態創建對象能力外,還具有序列化功能。
(定義并實現IsKindOf()、CreateObject()和Serialize())
posted on 2010-03-26 21:35 LynnRaymond 閱讀(644) 評論(0) 編輯 收藏 引用