MFC技術內幕簡結 (轉)

worldblog發表於2007-12-12
MFC技術內幕簡結 (轉)[@more@]

 

  中模型

  海風
在學習新知識時,我個人比較喜歡用聯想、比較和總結的方法去思考問題,解決問題,使一切未知的與已知的相聯絡,使一切已知的相似的相比較,從而總結他們的共性,整理與理清腦中亂糟糟的知識,從而達到提升。學習程式設計也不例外,在學程式設計過程中,我發現程式設計技術中有一種非常非常常用的技術:模型!訊息機制、文件檢視結構、動態生成以及COM都使用了一種相似的模型方法去解決問題。以下請聽小弟一一分析學習過程中的心得總結:
訊息機制:

  的進行是依靠外部發生的事件來,操作中有一個USER模組專門用來捕捉外圍裝置所發生的事件;比如當按下滑鼠時,USER模組就捕捉到一個滑鼠訊息,而且依據當時系統介面的情況確定響應此訊息的視窗,填充訊息結構MSG,並將這個滑鼠訊息放入系統訊息佇列中,GetMessage從訊息佇列中取得一個訊息,並依據訊息的內容把訊息發往特定的視窗函式,由視窗函式負責處理。每一條訊息,都對應著一組程式碼段,程式透過程式碼段來完成我們的任務;在MFC中,用類的資料成員封裝了我們要操作的資料,用函式成員封裝了要執行的程式碼段;於是一條訊息通常都對應一個函式。那麼,訊息怎麼知道自己該由那一個類中的那一個函式成員來執行呢?
 
首先,MFC為每一個能處理訊息的類設計了一個訊息對映表Message MAP,訊息對映表負責把這個類能處理的訊息和其處理函式“綁”在一塊。其次,我們把有繼承關係的類的訊息對映錶連結起來形成一張訊息網,透過依次比較有繼承關係的各個類中對映表中的內容(MessageID)尋找與訊息對應的處理函式,如果找到就其處理函式。如果在這張訊息網中找不到相等的MessageID(網中的類中都沒有定義響應這個訊息的函式),即分兩種情況處理:如果是命令訊息或由其它傳送過來的訊息,就跳到另外一張訊息網看看有沒有符合的選擇,跳完了專家們設定的該跳的網後仍然找不到符合的訊息,則呼叫系統預設的處理訊息的函式,找到了當然呼叫其處理函式了;如果是Windows訊息,則不用跳了,呼叫系統預設處理函式就行了。由此可見,在訊息傳遞的過程中,我們需要知道一些資訊:這個訊息是什麼型別以及如果要“跳網”時應該先跳到那一張網,跳到那一張網後仍然找不到後才放棄。因此,在有處理訊息能力的類的函式中定義了一個名為OnWndMsg函式分辨訊息的型別,定義了一個名為OnCmdMsg函式來決定跳網的路線(這條路線由專家們事先設定好了,無需我們勞神)。

剛才我們說,把訊息與其處理函式“綁”在一塊,把有繼承關係的類的訊息對映網連結起來,在MFC中我們是怎麼辦到的呢?MFC中設定了幾個宏來負責有關工作:DECLARE_MESSAGE_MAP/BEGIN_MESSAGE_MAP/ON_COMMAND/END_MESSAGE_MAP。基中DECLARE_MESSAGE_MAP這個宏裡主要包含了一個訊息對映表結構成員,一個獲取訊息對映表結構地址的函式及一個messageEntris成員。而BEGIN_MESSAGE_MAP/ ON_COMMAND/ END_MESSAGE_MAP三個宏負責填充訊息對映表的內容並依類的繼承關係建立一張訊息網。於是,當我們向類中加入一個新訊息,我們就不必在類申明中加入任何的東西了,因為DECLARE_MESSAGE_MAP裡有一個獲取訊息對映表結構地址的函式,我們就只需要向這個地址加入新的訊息項和其處理函式名就行了。為類定義一個新訊息就變成了做填空題。我把這種訊息與函式間的結構理解成一種模型;訊息與函式之間原本應該直接對應的,現在我們為了方便管理方便操作,人為地把它們分開,加入中間層(訊息對映表,專門管理訊息與處理函式的對應關係),以達到更強大的功能。這是一模型,間接模型,也是一種方法,即把A—C模型變成A—B—C模型。我也把做這種填空題看成一種模板,就好比畫龍點睛,龍已經畫好,我們只需點睛,龍就會飛了。以後我們會發現,在技術中,這是一種常用的解決問題的方法。

動態生成與型別識別:

無論是執行期型別識別,動態生成還是檔案的讀寫,都是用同一資料結構CRuntimeClass同一張稱為類別型錄網的網。類別型錄網是由CRuntimeClass連結而成的,在類中的申明由DECLARE_DYNAMIC宏封裝,連結工作由類外的IMPLEMENT_DYNAMIC宏封裝完成,當然根據不同的應用,CRuntimClass有不同的內容,所用的宏的名字也略有不同。他的工作原理和訊息機制有點相似,都是運用“指標”這一大特性完成橋樑工作,也都利用指標在類外另構一張網,集中管理資訊。這張網有什麼大用途呢?是這樣的,當我們在執行期要生成一個類時(比如在讀檔案時,裡面的資料要根據實際情況產生相應的物件我們才能讀),我們需要的資訊:類的名字,類的大小,類的建構函式,類與其它類的關係等等有關類的資訊,都將記錄在類別型錄網上;那麼當我們在執行期獲得一個(用字串表示的)類名時,我們就可以在這張網上找出對應的元素(透過比較網中成員的類名),然後呼叫其建構函式(裡面有個指標指向其建構函式),產生出物件。當然啦,執行期所產生的物件相應的類必需要在類別型錄網中才行,而這張網是由你一手一腳用IMPLEMENT_DYNAMIC建立起來的,裡面有些什麼類,你一清二楚。至於在如何建網,比較麻煩,反正有那兩個宏自動幫我們完成這些工作,我們只需要做做填空題,確定把那些類掛在網上即可。大家回頭想想,產生物件這件事,原本很簡單的,原本我們可以直接產生的,可是為了把程式活躍起來,為了在執行期也能動態生成(那時我們可不能像以前那隨隨便便加個語句就生成一個物件了,因為我們不可能再加語句到已經分發的上去),於是我們加了箇中間層,我們生成了一張網來集中管理型別資訊,這種解決問題的辦法是不是很高明!?我把動態生成的過程理解成一種模型,間接模型,也是一種方法,即把A—C模型變成A—B—C模型。我也把做這種填空題看成一種模板,就好比畫龍點睛,龍已經畫好,我們只需點睛,龍就會飛了。這一切都是為了為程式設計師提供了方便。

文件/檢視結構:

我們把文件看成一種資料,而檢視就是把這些資料按照我們想要的方式展示給我們看的視窗。如果一份資料只以一種方式顯示,如果我們只需顯示一種型別中的資料,那麼,資料與檢視一一對應也不需要玩什麼花招就能滿足我們的要求的。可是更多的時候,我們需要把資料以不同的方式(比如有關統計的資料,我們然望有線形圖,柱形圖,文字等多種方式顯示給我們看)顯示出來,我們更希望在同一程式中能處理數種不同檔案型別中的資料併為之提供不同的顯示介面,那麼一一對應好像就變得行不通了!怎麼辦?呵呵,我們加多一箇中間層就行了!這個中間層在MFC中叫作Document Template,由它來負責集中管理文件檢視之間的轉換,嗯,應該說是管理Document/View/Frame,Frame是包在View外面的視窗,他可以為View/Document提供專用的選單。為什麼資料能以不多的形式顯示呢?那是因為,據然資料能以一種形式顯示,就必定能以多種形式顯示!呵呵,好像有點強辭奪理,其實呢,因為有了箇中間層把文件檢視分開,那麼只要你按原資料多設計幾個顯示形式就行了,你需要什麼形式顯示,只要告訴Document Template,它就會體貼地幫你安排好(接上你想要的檢視形式,和此檢視專用的Frame)要做的工作了。同理,在同一主中為不同檔案型別中的資料提供專用的View和Frame也由 Document Template這個中界幫我們安排。理所當然的,在Document/View/Frame三位一體結構中,有一個連結串列把檔案型別(一個Document Template對應管理一種型別)連線起來,在顯示檔案中資料時也有個查詢過程,即先找到要顯示的檔案型別,再查詢要顯示View方式,之後動態生成專用的View/Frame。唉,裡面真是很複雜啊,想想都頭暈,不管啦,反正都是那些天才們的事。呵呵,不過我也把文件視/圖結構理解一種模型,也是一種方法,即把A—C模型變成A—B—C模型。

C++物件模型:

按我的理解,C++物件模型就是為了實現C++中物件導向的有關性質設計出來的有關類的資料成員、函式成員怎樣在中安排(才能更高效地實現物件導向的性質)的一種類的模型。為了實現多型,物件模型便不得不多設計了一張virtual table表,而且這張表要考慮單繼承多繼承所帶來的問題,還要考慮指向這張表的指標地址放在為物件分配得來的記憶體(以二進位制形式順序存放物件成員)的什麼位置才理想,派生類特別是有多個基類的派生類怎麼“繼承”這張虛表,同時也要考慮怎麼“繼承”基類的其它成員(很多時候,派生類都要複製所有基類的所有成員,因此而顯得浪費記憶體,確也無可奈何,所以,如果可以最好儘可能少派生類,並且一定要從最“基”的類派生)。也因為要實現多型,繼承,封裝等等性質,類的建構函式、解構函式,也變得複雜起來了,要考慮很多的問題,因為類與類之間不再獨立了,他們之間一但有聯絡,事情也複雜起來了。類的函式成員,資料成員,在這種情況下,日子也不好過了,他們在記憶體中的安排也就要求有更高的技巧性了。基本上,你可以從觀察C++物件模型中,知道為什麼C++會有多型,繼承這些特性,也知道C++語法為什麼是這樣子的,有什麼限制等等。在學《深度探索C++物件模型》這本書的時候,我有這樣的感覺:整本書各章間基本上是千絲萬縷,各個章節要求你理解的道理都差不多,都要回歸到記憶體分配這個話題上;所以,我建議,畫兩張有四五個類的詳細類圖,裡面包含有類的各種關係,那麼當我們看著這張圖來學這本書的時候就會變得輕鬆多了,雖然裡面也有圖,但有點亂,因此我畫了兩張類圖,以求精簡完整。

COM與登錄檔:

COM介面是什麼?按我現在的理解是,如我先前所說的模型中,把A—C模型變成A—B—C模型中的B中間層,A和C都是相對獨立的可執行程式,它是一種一種規則,用於把A和C連繫起來,為他們通訊提供方便。COM介面定義了一組虛擬函式,這些函式的功能都將在A(或B)中得以實現;介面也是類結構並且還是A(或B)的父類,根據C++多型性,我們知道,透過父類就可以呼叫派生類的函式(只要指向的是派生物件的地址)了。現在問題是,怎麼獲取派生類的物件地址呢?我並不清楚。因為C++擁有多繼承這一偉大性質,所以我們可以為A定義任義多個COM介面,利用巢狀類技術把COM介面的派生類插入A中以實現連線;比較複雜。

到目前為止,我仍然不能很好地理解登錄檔是什麼東東,計算機怎麼管理記憶體的;我看過不少那些號稱是內幕技術能夠知其所以然的書,但一講到這兩方面都是輕輕一筆帶過,於是我只能靠猜了。表面上看來,登錄檔是一些符號串的集合,而這些符號聽說是記錄應用程式中的一些資訊,應用程式需要記錄一些什麼樣的資訊呢?再讓我想想,我們使用程式時,好像新啟動的程式介面和上一次最後一次使用關閉時介面好像是一樣的(大小,位置,顏色等),為什麼會這樣子呢?莫非是登錄檔裡記錄差這些資訊,每次啟動程式時都從登錄檔裡取得這些資訊?我懷疑是,還沒得到證實。學COM的時候,書上好像也說過可執行程式檔案的路徑也記錄在登錄檔裡。我對記憶體管理很好奇,但對他的理解僅限於在學校課本里學到的,那遠遠不能滿足我的求知慾,以後有空必定再學。

  Application :

  剛開始學VC時,就常聽到有人說MFC就是類庫,對進行了簡單的封裝;呵呵,果真如此嗎?真是簡單的封裝嗎?不!至少最重要最關鍵的幾個類不是!MFC中幾個最最重要的類,如CWinApp,CView, CFrameWnd,CDocument,CDocTemplate等;絕不僅僅是“簡單的封裝”!它們之互相合作,透過訊息的流動而溝通,並且互相呼叫對方的函式,等等,互相呼叫函式?說起來好像挺不錯的,但做起來呢,要它們幾個類在互相呼叫而顯得亂七八糟的情況仍能條理清淅,共同完成幾乎我們所開發的所有的程式的基本框架,要考慮的問題何其的多!?Application Framework-----程式的基本框架?是的,我的理解就是這樣,Application Framework核心思想就基於面象對向開發程式的思想,為我們做出一套完美的基本程式模型,我們所開發的程式都以它為基礎。在面象對向語言C++之下,我們只需要從模型中原有的類中派生出自己的類,改寫一些虛擬函式,定義一些新函式,就可以方便地完成我們想要的功能了,很多麻煩的事(如處理Document/View/Frame中先前我們討論的麻煩事,程式初始化等)都由基本程式模型幫我們解決掉了。

  應用程式與的互動:

  固執的我一直想從C/C++語言出發去理解應用程式與使用者之間的互動的問題;在我腦裡常常想到的是程式裡有一個main(),裡面的程式程式碼是順序地執行的;於是應用程式可以同時甚至交錯地執行程式中不同地段的程式碼的問題就常常令我頭痛。現在,在我看了不少原始碼後,我覺悟了,原來,我們所開發出的程式,除去啟動程式的初始化操作中要順序執行的程式碼外,還有很多死的程式碼段,它們並沒有執行起來,和死的東西談判順序執行,我覺得自己很笨。不過,死是相對的,為我們啟用這些程式碼段提供了一線光明:向程式傳送訊息。訊息在作業系統的幫助下,送到應用程式中,應用程式就順序執行一系列死的程式碼,包括呼叫執行訊息所對應的函式,這時談順序執行才有意義。我們所傳送的訊息,都必須要有相應處理函式,這樣訊息才會有意義;各訊息的處理函式之間,程式設計師應該儘可能讓他們獨立起來,辦完自己的事就好了,函式執行完了就完了,不要與其它訊息處理函式扯上太多的關係,這就是模組化思想。程式功能模組與功能模組之間大談程式順序執行是沒意義的。而現在我還知道了順序執行的本質並沒有改變,只是因為有了個While迴圈用於捕捉訊息才會變成現在這個看起來有點不可思議的樣子。
  我常常刻意地追求知其所以然,可事實往往與願違。花太多的時間在這些理論上,而忽視了實踐,就變成了膚淺;最可恨的是,以前辛苦追尋而取得的收穫,不僅在實踐運用中沒什麼表現,而且還淡忘了;在發現API本身就是個大黑箱時,淚流滿面,到頭來,還是要查手冊,還是要清楚瞭解API函式引數及功能才能夠運用;使用API和使用那些控制元件一樣,不知它在裡面搞什麼鬼。暗箱操作,我們無能迴天,我們所能做的,也就只有儘可能詳細地瞭解有關介面與引數問題了。


 
  2002年7月10日


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752043/viewspace-991664/,如需轉載,請註明出處,否則將追究法律責任。

相關文章