From: CSDN
主要包括兩個
方面
一、程序的初始化
二、消息映射機(jī)制
以單文檔程序Test為例。程序自動生成的類為CAboutDlg、CMainFrame、CTestApp、CTestDoc、CTestView。
還有一個全局的應(yīng)用程序類對象CTestApp theApp。
流程如下:
1、CTestApp theApp; //初始化全局對象
2、因為Class CTestApp:public CWinApp,所以進(jìn)
入CWinApp::CWinApp(LPCTSTR lpszAppName)<位于AppCore.cpp中>
3、CTestApp::CTestApp() //調(diào)用自己的構(gòu)造函數(shù)
4、進(jìn)入WinMain()函數(shù),位于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、進(jìn)入AfxWinMain()函數(shù)中,這是MFC框
架函數(shù),位于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多態(tài)性原理,實際上調(diào)用的是//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中,由此進(jìn)入消息循環(huán)
② 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進(jìn)入注冊和創(chuàng)建窗口
//位于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-①-③
|
從③進(jìn)入,再經(jīng)CMainFrame::LoadFrame()進(jìn)
入注冊和創(chuàng)建窗口
(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)創(chuàng)建窗口CMainFrame::Create(),
之后再調(diào)用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()在調(diào)用CreateWindowEx()創(chuàng)
建真正的窗口對象之前,設(shè)置一個線程級的CBT Hook,該hook在
窗口創(chuàng)建完成后被調(diào)用,MFC在hook函
數(shù)中調(diào)用SetWindowLong()將
該窗口的窗口函數(shù)置換成AfxWndProc。
|
6、最后再經(jīng)5-①-②nReturnCode
= pThread->Run()進(jìn)入消息循環(huán)
======================================================================
消息循環(huán)機(jī)制分析
從nReturnCode
= pThread->Run()進(jìn)入。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))
{ //發(fā)
送消息到窗口,交由窗口過程函數(shù)處理
::TranslateMessage(&m_msgCur);
::DispatchMessage(&m_msgCur);
}
return
TRUE;
}
|
所有的窗口都只有一個相同的窗口過程窗口函數(shù)AfxWndProc,消
息傳遞圖如下所示:
OnWndMsg調(diào)用不成功,調(diào)用默認(rèn)窗口過程函數(shù)
|
BOOL CFrameWnd:: OnCmdMsg(…)
{
CView pView = GetActiveView();//得
到活動視指針。
if(pView-> OnCmdMsg(…))
return TRUE; //如果CView類
對象或其派生類對象已經(jīng)處理該消息,則返回。
……//否則,同
理向下執(zhí)行,交給文檔、框架、及應(yīng)用程序執(zhí)行自身的OnCmdMsg。
}//定義了消息在各個類中的傳遞順序
|
TranslateMessage
DispatchMessage
|
LRESULT CALLBACK AfxWndProc(HWND
hWnd,UINT nMsg,WPARAM wParam,
LPARAM lParam)
{ ……
CWnd*pWnd=CWnd::FromHandlePermanent(hWnd);
//把對句柄的操作轉(zhuǎn)換成對CWnd對
象。
ReturnAfxCallWndProc(pWnd,hWnd,nMsg,wParam,lParam);
}
|
有關(guān)消息的幾個宏
DECLARE_MESSAGE_MAP()宏
BEGIN_MESSAGE_MAP(theClass, baseClass)和END_MESSAGE_MAP()宏
弄懂MFC消息映射機(jī)制的最好辦法是將找出一個具體的實例,將這些宏展開,并找
出相關(guān)的數(shù)據(jù)結(jié)構(gòu)。
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()作下面三件
事:
定義一個長度不定的靜態(tài)數(shù)組變量_messageEntries[];
定義一個靜態(tài)變量messageMap;
定義一個虛擬函數(shù)GetMessageMap();
在DECLARE_MESSAGE_MAP()宏中,涉及到MFC中
兩個對外不公開的數(shù)據(jù)結(jié)構(gòu)
AFX_MSGMAP_ENTRY和AFX_MSGMAP。
為了弄清楚消息映射,有必要考察一下這兩個數(shù)據(jù)結(jié)構(gòu)的定義。
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)
};
結(jié)構(gòu)中各項的含義注釋已經(jīng)說明得很清楚了,這里不再多述,從上面的定義你是否看出,AFX_MSGMAP_ENTRY結(jié)
構(gòu)實際上定義了消息和處理此消息的動作之間的映射關(guān)系。因此靜態(tài)數(shù)組變量_messageEntries[]實
際上定義了一張表,表中的每一項指定了相應(yīng)的對象所要處理的消息和處理此消息的函數(shù)的對應(yīng)關(guān)系,因而這張表也稱為消息映射表。再看看AFX_MSGMAP的
定義。
(2)AFX_MSGMAP的
定義
struct AFX_MSGMAP
{
const AFX_MSGMAP* pBaseMap;
const AFX_MSGMAP_ENTRY* lpEntries;
};
不難看出,AFX_MSGMAP定義了一單向鏈表,
鏈表中每一項的值是一指向消息映射表的指針(實際上就是_messageEntries的
值)。通過這個鏈表,使得在某個類中調(diào)用基類的的消息處理函數(shù)很容易,因此,“父類的消息處理函數(shù)是子類的缺省消息處理函數(shù)”
就“順理成章”了。在后面的“MFC窗口的消息處理”一節(jié)中會對此作詳細(xì)的講解。
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 }
\
}; \
對應(yīng)BEGIN_MESSAGE_MAP()的定義可能不是一下子就看得明白,
不過不要緊,舉
一例子就很清楚了。對于BEGIN_MESSAGE_MAP(CView,
CWnd),VC預(yù)編譯器將其展開成下面
的形式:
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()則不過定義了一個表示映射表
結(jié)束的標(biāo)志項,我想大家對于這種簡單的技巧應(yīng)該是很熟悉的,無需多述。
到此為止,我想大家也已經(jīng)想到了象ON_COMMAND這
樣的宏的具體作用了,不錯它們只不過定義了一種類型的消息映射項,看看ON_COMMAND的定
義:
#define ON_COMMAND(id, memberFxn) \
{ WM_COMMAND, CN_COMMAND, (WORD)id,
(WORD)id, AfxSig_vv, (AFX_PMSG)&memberFxn
},
根據(jù)上面的定義,ON_COMMAND(ID_FILE_NEW, OnFileNew)將
被VC預(yù)編譯器展開
如下:
{WM_COMMAND, CN_COMMAND, (WORD)id, (WORD)id, AfxSig_vv,
(AFX_PMSG)&OnFileNew},
到此,MFC的消息映射機(jī)制已經(jīng)清楚了,現(xiàn)在提出并解答兩個問題以作為對這一節(jié)
的小結(jié)。
為什么不直接使用虛擬函數(shù)實現(xiàn)消息處理函數(shù)呢?這是一個GOOD QUESTION。
前面已經(jīng)說過,MFC的設(shè)計者們在設(shè)計MFC時
有一個很明確的目標(biāo),就是使得“MFC的代碼盡可能小,速度盡可能快”,如果采用虛擬函數(shù),那么對
于所有的窗口消息,都必須有一個與之對應(yīng)的虛擬函數(shù),因而對每一個從CWnd派
生的類而言,都會有一張很大的虛擬函數(shù)表vtbl。但是在實際應(yīng)用中,
一般只對少數(shù)的消息進(jìn)行處理,大部分都交給系統(tǒng)缺省處理,所以表中的大部分項都是無用項,這樣做就浪費了很多內(nèi)存資源,這同MFC設(shè)
計者們的設(shè)計目標(biāo)是相違背的。當(dāng)然,MFC所使用的方法只是解決這類問題的方式之一,不排除還有其
他的解決方式,但就我個人觀點而言,這是一種最好的解決方式,體現(xiàn)了很高的技巧性,值得我們學(xué)習(xí)。
至于這第二個問題,是由上面的問題引申出來的。如果在子類和父類中出現(xiàn)了相同的消息出來函數(shù),VC編
譯器會怎么處理這個問題呢?VC不會將它們看作錯誤,而會象對待虛擬函
數(shù)類似的方式去處理,但對于消息處理函數(shù)(帶afx_msg前
綴),則不會生成虛擬函數(shù)表vtbl。
======================================================================
RTTI實現(xiàn)機(jī)制
C++設(shè)計者在C++使用的早期并沒有意識到RTTI(運
行時類型檢查)的重要性,后來
隨作框架結(jié)構(gòu)的類庫出現(xiàn)及其應(yīng)用越來越廣泛,RTTI就
變得越來越重要了。例如下面的這個語句:
CWnd *pWnd;
任何人都知道對象p是CWnd類
型的指針。但是如果有一個類CView是從CWnd派
生來的,對于下面的語句:
CWnd* CreateView()
{
return new CView;
}
對于使用CreateView()的
用戶而然,pWnd = CreateView(),
他如何確定pWnd所指向的對象的真正類型呢?因此,必須有一個能夠在
運行時刻就能夠確定指針對象類型的方法,比如給每一個類型的對象均添加一個IsKindOf()之
類的方法,通過此方法判斷指針對象的類型。
后來,RTTI被加入了C++的
規(guī)范,成為C++一個內(nèi)置的特性。
在MFC的設(shè)計者們設(shè)計MFC的
時候,C++規(guī)范中并沒有包含RTTI,但
是他們很早就意識到這個問題,所以他們以一種獨特的方式在MFC中實現(xiàn)RTTI,
采用這種方式實現(xiàn)的RTTI對于某個對象而言并不是必須的,也就是說,MFC的
設(shè)計者們并不將RTTI強(qiáng)加于用戶所設(shè)計的類型上,而是讓用戶根據(jù)自己的需要選擇是否他所設(shè)計的類
型需要RTTI。因而這種方式比C++規(guī)范
中內(nèi)置的RTTI更靈活。
MFC的設(shè)計者們在MFC中采用下面的的方
法來實現(xiàn)RTTI:
設(shè)計一個基類CObject,
在CObject中增加RTTI功
能,任何一個類型,如果需
要具有RTTI功能,就必須直接或間接派生于CObject
采用宏實現(xiàn)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定義了整個從其
派生的家族的所有成員所具有的兩個基本的能力:
運行時的動態(tài)類型檢查(RTTI)能力和序列化能力。在早期的C++版
本中,沒有規(guī)定RTTI,但MFC的作者們早
就未撲先知,以這種構(gòu)架的形式定義并實現(xiàn)RTTI。體現(xiàn)RTTI的
是CObject中的兩個成員函數(shù):
virtual CRuntimeClass * GetRuntimeClass()
const;
BOOL IsKindOf(const CRuntimeClass
*pClass) const;
其中,前一個函數(shù)用來訪問存儲RTTI信息的一個CRuntimeClass類
型的結(jié)構(gòu),后一個函數(shù)供在運行時刻進(jìn)行類型判斷。我們先來看看CRuntimeClass結(jié)
構(gòu)的定義,看看它究竟保存了哪些類型信息。
<<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保存類的實例數(shù)據(jù)所占內(nèi)存的的大
小。我們重點要關(guān)注的是m_pBaseClass成員,它是指向名稱為m_lpszClassName的
類的基類的CRuntimeClass的指
針,因此,CRuntimeClass就形成了一個繼承鏈表,這個鏈表
記錄了某一族類的繼承關(guān)系。
RTTI的實現(xiàn):
實現(xiàn)RTTI的除了上面兩個函數(shù)外,還有幾個相關(guān)的宏。我們先看看GetRuntimeClass()和IsKindOf()的
實現(xiàn).
1.GetRuntimeClass()的
實現(xiàn)
CRuntimeClass* CObject::GetRuntimeClass()
const
{
return RUNTIME_CLASS(CObject);
}
關(guān)鍵就在RUNTIME_CLASS這個宏上,RUNTIME_CLASS宏
的實現(xiàn)如下:
#define RUNTIME_CLASS(class_name) ((CRuntimeClass*)(&class_name::class##class_name))
將宏展開,上面的實現(xiàn)就變成:
CRuntimeClass* CObject::GetRuntimeClass()
const
{
return (CRuntimeClass*)(&CObject::classCObject);
}
也就是說,它返回CObject類的一個static型
的成員classCObject。
2.IsKindOf()的
實現(xiàn)
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);
}
前兩行我們不管它,關(guān)鍵在于最后一行pClassThis->IsDerivedFrom(pClass),歸
根結(jié)底就是調(diào)用CRuntimeClass的IsDerivedFrom()方
法。下面是CRuntimeClass的成員IsDerivedFrom()的
實現(xiàn):
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
}
關(guān)鍵是上面的一段循環(huán)代碼:
while (pClassThis !=
NULL)
{
if (pClassThis == pBaseClass)
return TRUE;
pClassThis = pClassThis->m_pBaseClass;
}
它從繼承鏈表的某一節(jié)點this開始,向后搜索比較,確定繼承關(guān)系。
將到這里,或許有人要問,這些CRuntimeClass結(jié)
構(gòu)是如何產(chǎn)生的呢?這是一個很好的問題,解決了這個問題,就完全清楚了MFC中RTTI的
實現(xiàn)。使用過Visual C++開發(fā)程序的人都應(yīng)該記得DECLARE_DYNAMIC和IMPLEMENT_DYNAMIC這
兩個宏,它們分別用來定義相應(yīng)類的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宏用來在類的定義中定義靜態(tài)CRuntimeClass變
量和虛擬GetRuntimeClass()函
數(shù)。可以推斷,IMPLEMENT_DYNAMIC宏一定是用來初始化該靜態(tài)變量和實現(xiàn)GetRuntimeClass()函
數(shù),。不錯,正是這樣!
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)展開如下:
//下面展開的代碼用來初始化靜態(tài)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
}
//下面的代碼用來實現(xiàn)GetRuntimeClass()函
數(shù)
CRuntimeClass* CView::GetRuntimeClass()
const
{ return (CRuntimeClass*)(&CView::classCView);}
總的來說,同RTTI有關(guān)的宏有下面幾對:
DECLARE_DYNAMIC和IMPLEMENT_DYNAMIC
這一對宏能夠提供運行是類型判斷能力。(定義并實現(xiàn)IsKindOf())
DECLARE_DYNCREATE和IMPLEMENT_DYNCREATE
這一對宏除了能夠提供類型判斷能力外,還能夠提供動態(tài)創(chuàng)建對象的能力。
(定義并實現(xiàn)IsKindOf()和CreateObject())
DECLARE_SERIAL和IMPLEMENT_SERIAL
這一對宏除了提供類型判斷能力、動態(tài)創(chuàng)建對象能力外,還具有序列化功能。
(定義并實現(xiàn)IsKindOf()、CreateObject()和Serialize())