VC++訊息映射的思考
作者:郝慶欣
在學習VC++的時候,大家都不可避免的用到訊息映射。我們都知道C++是一種物件導向的程式設計語言,VC++中為什麼這樣來實現訊息映射呢?
首先要明白一個包含了訊息處理的Windows程式是如何工作的。
一般來說一個包含了訊息處理的Windows程式至少要包含兩個函數
第一個:
int WINAPI WinMain(
HINSTANCE hInstance, // handle to current instance
HINSTANCE hPrevInstance, // handle to previous instance
LPSTR lpCmdLine, // command line
int nCmdShow // show state
);
第二個:
long FAR PASCAL WndProc(HWND hWnd,WORD message,WORD wParam,LONG lParam);
我們不必糾纏程式實現的細節,只要明白在第一個函數WinMain中要註冊WndProc函數,通俗一些的理解就是WinMain告訴Windows系統,聽著,我知道你要產生很多訊息,我這裡有一個WndProc函數負責處理你傳遞來的各種訊息。當然訊息的格式都是系統規定好的。
其次要明白C++中是如何?多態性的。
我們知道多態性實現的關鍵是晚綁定(或者稱為後期綁定),其實質就是編譯器並沒有在編譯期間指定調用函數的絕對位址,而是指定了某個類內部該函數的位移地址。
為了實現上面的功能,編譯器為我們作了手腳
1、 在每個帶有虛函數的類中,編譯器秘密放置了一個指標,稱為Vpointer
2、 當系統運行時,為每個類建立一個VTABLE,其中包含了可以調用虛函數地址。
3、 Vpointer出始化,指向VTABLE,通過在Vtable中位移,來找到正確的需要調用的函數地址。
然後是MFC對Window API進行的封裝
當我們利用MFC架構開發程式的時候,尤其是開發介面應用程式的時候,必定要用到CWnd或者派生於CWnd的類。根據物件導向的設計原則,對於CWnd的一些通用函數,例如視窗大學改變(OnSize),視窗移動(OnMove),最好是在CWnd中聲明為虛函數,然後在繼承的類裡面重載他們。但是,這樣以來,每個相關的衍生類別都要有一個Vpointer和一套記錄Vtable,而CWnd中通用函數是如此至多,CWnd的衍生類別也很多,必然會導致系統在運行是佔用過多的資源(記憶體),這樣顯然是不合適的。
那麼MFC是如何?的呢?
答案就是在CWnd基類中儘可能的少用虛函數,採用訊息映射機制來代替。
大家可以看一下CWnd的類中的函數,就會發現這一點。
CWnd::OnMove
afx_msg void OnMove( int x, int y );
上面這個函數就不是虛函數。
最後的問題訊息映射是如何?的呢?
用一句話說,就是利用宏定義來實現面向過程的訊息處理。
例如在VC中有如下的訊息映射宏。
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
//{{AFX_MSG_MAP(CMainFrame)
ON_WM_CREATE()
//}}AFX_MSG_MAP
ON_COMMAND(ID_FONT_DROPDOWN, DoNothing)
END_MESSAGE_MAP()
經過編譯後,代碼被替換為如下形式(這隻是作講解,實際情況比這複雜得多):
//BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
CMainFrame::newWndProc(...)
{
switch(...)
{
//{{AFX_MSG_MAP(CMainFrame)
// ON_WM_CREATE()
case(WM_CREATE):
OnCreate(...);
break;
//}}AFX_MSG_MAP
// ON_COMMAND(ID_FONT_DROPDOWN, DoNothing)
case(WM_COMMAND):
if(HIWORD(wP)==ID_FONT_DROPDOWN)
{
DoNothing(...);
}
break;
//END_MESSAGE_MAP()
}
}
這樣,VC++就消除了對部分虛擬函數的需要,從而節省了記憶體空間。