rel="file-list" href="file:///c:%5cdocume%7e1%5cddnw%5clocals%7e1%5ctemp%5cmsohtml1%5c01%5cclip_filelist.xml">多型的實現機制有兩種,一是通過查詢絕對位置表,二是查詢名稱表;兩者各有優缺點,那麼為什麼
mfc的訊息對映採用了第二種方法,而不是
c++使用的第一種呢?因為在
mfc的
gui類庫是乙個龐大的繼承體系,而裡面的每個類有很多成員函式(只說訊息反映相關的成員函式啊),而且在派生類中,需要改寫的也比較少
(我用來做練習的程式就是那麼一兩個,呵呵
)。那麼用
c++的虛函式的實現機制會導致什麼問題呢?就是大量虛表的建立使得空間浪費掉很多。 嗯
…怎麼辦呢?於是各大
c++名庫(比如qt,
mfc,
vcl…
)在訊息對映的實現方面,拋開了虛函式的方式,而用了第二種方法:查詢名稱表,其原理五花八門,各顯神通,讓我想到了春秋時代,各國諸侯置周天子不顧,挾天子令諸侯,各自為政的階段,呵呵
~現在先說
mfc的做法:
mfc訊息對映機制的原理,也就是
mfc是怎麼做到乙個訊息來了,就呼叫相應的成員函式的?(在
vc程式設計裡面用的是訊息迴圈機制,那比較好理解,就是在訊息處理程式裡面來個大大的
swicth…
)好了,在
c++ stl
成熟的現在,很多人都會想到用
map來匹配(據我所知,有些公司很喜歡這樣用,拋開了
mfc的做法),但是當時
stl沒有流行,所以
mfc設計者們就來個鍊錶查詢。
先看用法:
首先,要用訊息處理的類,必須要繼承自
ccmdtarget
類;然後,在類的宣告中有如下的巨集:
declare_message_map()
和需要實現的訊息對映
然後,在類的實現檔案中,有如下巨集:
begin_message_map(…), … end_message_map()
具體例子如下所示:
//標頭檔案中:
class
cmainframe : public cframewnd;//
實現檔案中:
begin_message_map(cmainframe, cframewnd)
on_wm_create()
end_message_map()
那些巨集展開之後如下所示:
class
cmainframe : public cframewnd
;//
下10行為巨集
begin_message_map(cmainframe, cframewnd)
的展開const
afx_msgmap* cmainframe::getmessagemap() const
const
afx_msgmap* cmainframe::getthismessagemap()
,//
下5行為巨集
end_message_map()
的展開
};
static
const afx_msgmap messagemap = ;
return &messagemap;
}上面這些幾乎全是資料結構的初始化**,先不管這裡面的結構放的是什麼內容,先看類宣告那個巨集展開的兩個函式究竟在mfc框架的那個地方使用?(先看過程,在看資料結構,這是物件導向分析的不二法門~)
下面,說一下別人總結的mfc流程中的訊息分派,(參考文章會放在附錄裡面的,這只是筆記嘛,呵呵)
注意:我安裝的是vs2005,mfc原始碼在安裝的目錄下面的 vc/atlmfc/src/mfc目錄裡面 1、
先假定mfc的訊息入口點是cwnd::windowproc函式(至於是如何流入的,請參考其他文章),其**就好像vc程式設計裡面那個winmain函式的訊息迴圈部分差不多:
lresult cwnd::windowproc(uint message, wparam wparam, lparam lparam)
看,就是看一下該訊息在onwndwsg裡是否找到對應的處理函式,如果沒找到,用defwindowproc處理; 2、
那麼,這個onwndwsg裡面就是呼叫以上巨集展開的那個函式來取得相關的訊息對映滴~簡化過後的**如下:
bool cwnd::onwndmsg(uint message, wparam wparam, lparam lparam, lresult* presult)
;再看cmainframe中的**:
static
const afx_msgmap messagemap = ;
就知道:這個**其實是乙個鍊錶,放的是基類的getmassagemap函式指標,和當前類的真正**_messageentries的入口位址。再看_messageentries裡面放的又是什麼:
nmessage
ncode
nid
nlastid
nsig
npfn
wm_create
0
0
0
afxsig_is
&cmainclass::oncreate
0
0
0
0
afxsig_end
0
好了,現在看到了wm_create 就連著&cmainclass::oncreate,再結合上面cwnd::onwndmsg的**:
for (; pmessagemap != null; pmessagemap = pmessagemap->pbasemap)
if (message < 0xc000)
if ((lpentry = afxfindmessageentry(pmessagemap->lpentries, message, 0, 0)) != null)
goto ldispatch;
就可以推理得到,afxfindmessageentry就是在找message和_messageentries.nmessage的相等,如果不等,就往基類裡面找: pmessagemap = pmessagemap->pbasemap
找到了就跳到ldispatch,其中,有:mmf.pfn = lpentry->npfn;這個就是放著該訊息處理函式的內容。 3、
總結:
其實那些巨集的作用就是用來初始化那個鍊錶messagemap 和當前類處理函式的**_messageentries,當框架**執行到pmessagemap = getmessagemap();時候,由於虛函式的作用,使得當面執行當前視窗cmainframe的getmassagemap,把cmainframe的**給pmessagemap,然後再從派生類往基類一層層找響應的處理函式。
這與c++虛函式機制相比,其好處是可以省空間,每個類裡面只有自己重寫的函式資訊,其他資訊在基類裡面找;
不好的地方就是搜鍊錶,從當前類往基類上搜,時間上可能久一點;
mfc訊息分派的基本原理就是這樣的,但是還有很多的細節沒有深入,比如那些不同的成員函式有不同的引數,他們是怎麼傳遞的,怎麼在**裡面統一引數的資訊...這些看下面的參考文獻,有較為詳細的說明。
4、補充:用的框架**裡面,最顯著的是一種模式:模板方法模式(template method pattern)。具體
的說明請參考其他文章。
這裡為了補充,舉乙個例子說明是怎麼呼叫的:
class a
;protected:
static void printthis();
private:
virtual void print();
};class b : public a
private:
void print()
};如下語句輸出什麼:
b b;
a *pa = &b;
pa->callprint();
也許你知道輸出的是「this is a b object!/n」,但是,知道為什麼嘛?參考文章中有詳細的講解,會令你滿意的。
(注意上面例子中的virtual和訪問許可權,試著利用虛函式的實現機制來解釋他是怎麼作用的。)
參考文章:
、mfc
訊息分派:http://blog.csdn.net/linzhengqun/archive/2007/11/28/1905671.aspx
、mfc
教程之訊息對映的實現:http://www.vczx.com/tutorial/mfc/mfc4.php
、與大蝦對話:領悟設計模式:http://www.myfaq.com.cn/a200508/2005-08-07/183608.html
MFC訊息對映的原理
多型的實現機制有兩種,一是通過查詢絕對位置表,二是查詢名稱表 兩者各有優缺點,那麼為什麼 mfc的訊息對映採用了第二種方法,而不是 c 使用的第一種呢?因為在 mfc的 gui類庫是乙個龐大的繼承體系,而裡面的每個類有很多成員函式 只說訊息反映相關的成員函式啊 而且在派生類中,需要改寫的也比較少 我...
MFC的訊息對映原理
本文要求對c 語法比較熟悉 特別是虛函式的使用 若不熟悉建議參閱 c 語法詳解 一書,電子工業出版社出版 1 訊息對映 就是把指定的訊息交給指定的函式進行處理的方法,這樣就形成了乙個 訊息,處理函式 對。2 本文有時會使用表示 訊息,處理函式 對。一 共用體 union 的使用 1 共用體可以實現以...
MFC訊息對映
run這個函式來建立和處理訊息迴圈 bool afxapi afxinternalpumpmessage return true 顯而易見,mfc中處理訊息也是利用了win32下的訊息處理 那麼還是這樣的結構 typedef struct tagmsg msg 有了這個概念之後我們知道,mfc通過訊...