1. 程式人生 > >清風一吹(清風部落格)

清風一吹(清風部落格)

經常在VC 中聽別人講MFC但一直不知道到底是什麼意思,MFC簡單來說就是VC的類庫.
MFC是一個程式設計框架

MFC (Microsoft Foundation Class Library)中的各種類結合起來構成了一個應用程式框架,它的目的就是讓程式設計師在此基礎上來建立Windows下的應用程式,這是一種相對SDK來說更為簡單的方法。因為總體上,MFC框架定義了應用程式的輪廓,並提供了使用者介面的標準實現方法,程式設計師所要做的就是通過預定義的介面把具體應用程式特有的東西填入這個輪廓。Microsoft Visual C++提供了相應的工具來完成這個工作:AppWizard可以用來生成初步的框架檔案(程式碼和資源等);資源編輯器用於幫助直觀地設計使用者介面;ClassWizard用來協助新增程式碼到框架檔案;最後,編譯,則通過類庫實現了應用程式特定的邏輯。


封裝

構成MFC框架的是MFC類庫。MFC類庫是C++類庫。這些類或者封裝了Win32應用程式程式設計介面,或者封裝了應用程式的概念,或者封裝了OLE特性,或者封裝了ODBC和DAO資料訪問的功能,等等,分述如下。

(1)對Win32應用程式程式設計介面的封裝

用一個C++ Object來包裝一個Windows Object。例如:class CWnd是一個C++ window object,它把Windows window(HWND)和Windows window有關的API函式封裝在C++ window object的成員函式內,後者的成員變數m_hWnd就是前者的視窗控制代碼。

(2)對應用程式概念的封裝

使用SDK編寫Windows應用程式時,總要定義視窗過程,登記Windows Class,建立視窗,等等。MFC把許多類似的處理封裝起來,替程式設計師完成這些工作。另外,MFC提出了以文件-檢視為中心的程式設計模式,MFC類庫封裝了對它的支援。文件是使用者操作的資料物件,檢視是資料操作的視窗,使用者通過它處理、檢視資料。

(3)對COM/OLE特性的封裝

OLE建立在COM模型之上,由於支援OLE的應用程式必須實現一系列的介面(Interface),因而相當繁瑣。MFC的OLE類封裝了OLE API大量的複雜工作,這些類提供了實現OLE的更高階介面。

(4)對ODBC功能的封裝

以少量的能提供與ODBC之間更高階介面的C++類,封裝了ODBC API的大量的複雜的工作,提供了一種資料庫程式設計模式。


繼承

首先,MFC抽象出眾多類的共同特性,設計出一些基類作為實現其他類的基礎。這些類中,最重要的類是CObject和CCmdTarget。CObject是MFC的根類,絕大多數MFC類是其派生的,包括CCmdTarget。CObject 實現了一些重要的特性,包括動態類資訊、動態建立、物件序列化、對程式除錯的支援,等等。所有從CObject派生的類都將具備或者可以具備CObject所擁有的特性。CCmdTarget通過封裝一些屬性和方法,提供了訊息處理的架構。MFC中,任何可以處理訊息的類都從CCmdTarget派生。

針對每種不同的物件,MFC都設計了一組類對這些物件進行封裝,每一組類都有一個基類,從基類派生出眾多更具體的類。這些物件包括以下種類:視窗物件,基類是CWnd;應用程式物件,基類是CwinThread;文件物件,基類是Cdocument,等等。

程式設計師將結合自己的實際,從適當的MFC類中派生出自己的類,實現特定的功能,達到自己的程式設計目的。


虛擬函式和動態約束

MFC以“C++”為基礎,自然支援虛擬函式和動態約束。但是作為一個程式設計框架,有一個問題必須解決:如果僅僅通過虛擬函式來支援動態約束,必然導致虛擬函式表過於臃腫,消耗記憶體,效率低下。例如,CWnd封裝 Windows視窗物件時,每一條Windows訊息對應一個成員函式,這些成員函式為派生類所繼承。如果這些函式都設計成虛擬函式,由於數量太多,實現起來不現實。於是,MFC建立了訊息對映機制,以一種富有效率、便於使用的手段解決訊息處理函式的動態約束問題。

這樣,通過虛擬函式和訊息對映,MFC類提供了豐富的程式設計介面。程式設計師繼承基類的同時,把自己實現的虛擬函式和訊息處理函式嵌入MFC的程式設計框架。MFC程式設計框架將在適當的時候、適當的地方來呼叫程式的程式碼。本書將充分的展示MFC呼叫虛擬函式和訊息處理函式的內幕,讓讀者對MFC的程式設計介面有清晰的理解。


MFC的巨集觀框架體系

如前所述,MFC實現了對應用程式概念的封裝,把類、類的繼承、動態約束、類的關係和相互作用等封裝起來。這樣封裝的結果對程式設計師來說,是一套開發模板(或者說模式)。針對不同的應用和目的,程式設計師採用不同的模板。例如,SDI應用程式的模板,MDI應用程式的模板,規則DLL應用程式的模板,擴充套件DLL應用程式的模板,OLE/ACTIVEX應用程式的模板,等等。

這些模板都採用了以文件-視為中心的思想,每一個模板都包含一組特定的類。典型的MDI應用程式的構成將在下一節具體討論。

為了支援對應用程式概念的封裝,MFC內部必須作大量的工作。例如,為了實現訊息對映機制,MFC程式設計框架必須要保證首先得到訊息,然後按既定的方法進行處理。又如,為了實現對DLL程式設計的支援和多執行緒程式設計的支援,MFC內部使用了特別的處理方法,使用模組狀態、執行緒狀態等來管理一些重要資訊。雖然,這些內部處理對程式設計師來說是透明的,但是,懂得和理解MFC內部機制有助於寫出功能靈活而強大的程式。


總之,MFC封裝了Win32 API,OLE API,ODBC API等底層函式的功能,並提供更高一層的介面,簡化了Windows程式設計。同時,MFC支援對底層API的直接呼叫。

MFC提供了一個Windows應用程式開發模式,對程式的控制主要是由MFC框架完成的,而且MFC也完成了大部分的功能,預定義或實現了許多事件和訊息處理,等等。框架或者由其本身處理事件,不依賴程式設計師的程式碼;或者呼叫程式設計師的程式碼來處理應用程式特定的事件。

MFC是C++類庫,程式設計師就是通過使用、繼承和擴充套件適當的類來實現特定的目的。例如,繼承時,應用程式特定的事件由程式設計師的派生類來處理,不感興趣的由基類處理。實現這種功能的基礎是C++對繼承的支援,對虛擬函式的支援,以及MFC實現的訊息對映機制。


MDI應用程式的構成

本節解釋一個典型的MDI應用程式的構成。

用AppWizard產生一個MDI工程t(無OLE等支援),AppWizard建立了一系列檔案,構成了一個應用程式框架。這些檔案分四類:標頭檔案(.h),實現檔案(.cpp),資原始檔(.rc),模組定義檔案(.def),等。


構成應用程式的物件

圖1-1解釋了該應用程式的結構,箭頭表示資訊流向。



從CWinApp、CDocument、CView、CMDIFrameWnd、CMDIChildWnd類對應地派生出CTApp、CTDoc、CTView、CMainFrame、CChildFrame五個類,這五個類的例項分別是應用程式物件、文件物件、視物件、主框架視窗物件和文件邊框視窗物件。主框架視窗包含了視視窗、工具條和狀態列。對這些類或者物件解釋如下。

(1)應用程式

應用程式類派生於CWinApp。基於框架的應用程式必須有且只有一個應用程式物件,它負責應用程式的初始化、執行和結束。

(2)邊框視窗

如果是SDI應用程式,從CFrameWnd類派生邊框視窗類,邊框視窗的客戶子視窗(MDIClient)直接包含視視窗;如果是MDI應用程式,從CMDIFrameWnd類派生邊框視窗類,邊框視窗的客戶子視窗(MDIClient)直接包含文件邊框視窗。

如果要支援工具條、狀態列,則派生的邊框視窗類還要新增CToolBar和CStatusBar型別的成員變數,以及在一個OnCreate訊息處理函式中初始化這兩個控制視窗。

邊框視窗用來管理文件邊框視窗、視視窗、工具條、選單、加速鍵等,協調半模式狀態(如上下文的幫助(SHIFT+F1模式)和列印預覽)。

(3)文件邊框視窗

文件邊框視窗類從CMDIChildWnd類派生,MDI應用程式使用文件邊框視窗來包含視視窗。

(4)文件

文件類從CDocument類派生,用來管理資料,資料的變化、存取都是通過文件實現的。視視窗通過文件物件來訪問和更新資料。

(5)視

視類從CView或它的派生類派生。視和文件聯絡在一起,在文件和使用者之間起中介作用,即視在螢幕上顯示文件的內容,並把使用者輸入轉換成對文件的操作。

(6)文件模板

文件模板類一般不需要派生。MDI應用程式使用多文件模板類CMultiDocTemplate;SDI應用程式使用單文件模板類CSingleDocTemplate。

應用程式通過文件模板類物件來管理上述物件(應用程式物件、文件物件、主邊框視窗物件、文件邊框視窗物件、視物件)的建立。


構成應用程式的物件之間的關係


這裡,用圖的形式可直觀地表示所涉及的MFC類的繼承或者派生關係,如圖1-2所示意。

圖1-2所示的類都是從CObject類派生出來的;所有處理訊息的類都是從CCmdTarget類派生的。如果是多文件應用程式,文件模板使用CMultiDocTemplae,主框架視窗從CMdiFarmeWnd派生,它包含工具條、狀態列和文件框架視窗。文件框架視窗從CMdiChildWnd派生,文件框架視窗包含視,視從CView或其派生類派生。


構成應用程式的檔案

通過上述分析,可知AppWizard產生的MDI框架程式的內容,所定義和實現的類。下面,從檔案的角度來考察AppWizard生成了哪些原始碼檔案,這些檔案的作用是什麼。表1-1列出了AppWizard所生成的標頭檔案,表1-2列出了了AppWizard所生成的實現檔案及其對標頭檔案的包含關係。

 

表1-1 AppWizard所生成的標頭檔案


標頭檔案
用途

stdafx.h
標準AFX標頭檔案

resource.h
定義了各種資源ID

t.h
#include "resource.h"

定義了從CWinApp派生的應用程式物件CTApp


childfrm.h
定義了從CMDIChildWnd派生的文件框架視窗物件CTChildFrame

mainfrm.h
定義了從CMDIFrameWnd派生的框架視窗物件CMainFrame

tdoc.h
定義了從CDocument派生的文件物件CTDoc

tview.h
定義了從CView派生的檢視物件CTView




 

表1-2 AppWizard所生成的實現檔案


實現檔案
所包含的標頭檔案
實現的內容和功能

stdafx.cpp
#include "stdafx.h"
用來產生預編譯的型別資訊。

t.cpp
# include "stdafx.h"

# include "t.h"


# include "MainFrm.h"

# include "childfrm.h"

#include "tdoc.h"

#include "tview.h"

定義CTApp的實現,並定義CTApp型別的全域性變數theApp。

childfrm.cpp
#inlcude "stdafx.h"

#include "t.h"


#include “childfrm.h”
實現了類CChildFrame

childfrm.cpp
#inlcude "stdafx.h"

#include "t.h"


#include "childfrm.h"
實現了類CMainFrame

tdoc.cpp
# include "stdafx.h"

# include "t.h"


# include "tdoc.h"
實現了類CTDoc

tview.cpp

# include "stdafx.h"

# include "t.h"


# include "tdoc.h"

# include "tview.h"

實現了類CTview





 

 

從表1-2中的包含關係一欄可以看出:

CTApp 的實現用到所有的使用者定義物件,包含了他們的定義;CView 的實現用到CTdoc;其他物件的實現只涉及自己的定義;

當然,如果增加其他操作,引用其他物件,則要包含相應的類的定義檔案。

對預編譯標頭檔案說明如下:

所謂標頭檔案預編譯,就是把一個工程(Project)中使用的一些MFC標準標頭檔案(如Windows.H、Afxwin.H)預先編譯,以後該工程編譯時,不再編譯這部分標頭檔案,僅僅使用預編譯的結果。這樣可以加快編譯速度,節省時間。

預編譯標頭檔案通過編譯stdafx.cpp生成,以工程名命名,由於預編譯的標頭檔案的字尾是“pch”,所以編譯結果檔案是projectname.pch。

編譯器通過一個頭檔案stdafx.h來使用預編譯標頭檔案。stdafx.h這個標頭檔案名是可以在project的編譯設定裡指定的。編譯器認為,所有在指令#include "stdafx.h"前的程式碼都是預編譯的,它跳過#include "stdafx. h"指令,使用projectname.pch編譯這條指令之後的所有程式碼。

因此,所有的CPP實現檔案第一條語句都是:#include "stdafx.h"。

另外,每一個實現檔案CPP都包含了如下語句:

#ifdef _DEBUG

#undef THIS_FILE

static char BASED_CODE THIS_FILE[] = __FILE__;

#endif

這是表示,如果生成除錯版本,要指示當前檔案的名稱。__FILE__是一個巨集,在編譯器編譯過程中給它賦值為當前正在編譯的檔名稱。