MFC訊息對映的原理

2021-04-24 08:14:53 字數 4282 閱讀 2273

多型的實現機制有兩種,一是通過查詢絕對位置表,二是查詢名稱表;兩者各有優缺點,那麼為什麼

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的訊息對映原理

本文要求對c 語法比較熟悉 特別是虛函式的使用 若不熟悉建議參閱 c 語法詳解 一書,電子工業出版社出版 1 訊息對映 就是把指定的訊息交給指定的函式進行處理的方法,這樣就形成了乙個 訊息,處理函式 對。2 本文有時會使用表示 訊息,處理函式 對。一 共用體 union 的使用 1 共用體可以實現以...

MFC訊息對映的原理 筆記

rel file list href file c 5cdocume 7e1 5cddnw 5clocals 7e1 5ctemp 5cmsohtml1 5c01 5cclip filelist.xml 多型的實現機制有兩種,一是通過查詢絕對位置表,二是查詢名稱表 兩者各有優缺點,那麼為什麼 mfc...

MFC訊息對映

run這個函式來建立和處理訊息迴圈 bool afxapi afxinternalpumpmessage return true 顯而易見,mfc中處理訊息也是利用了win32下的訊息處理 那麼還是這樣的結構 typedef struct tagmsg msg 有了這個概念之後我們知道,mfc通過訊...