VC|了解模態對話框與非模態對話框的內在機制及內存分配差異

MFC中對話框有兩種形式,一個是模態對話框(model dialog box),一個是非模態對話框(modeless dialog box)。

模態對話框是一種阻塞式的對話框,即沒有處理完該對話框,不能對其他地方進行操作。非模態對話框和模態對話框相反,它不是阻塞型的,即你可以同時操作其他的窗口,比如父窗口。

模態對話框在關閉對話框(OnOk、OnCancel、OnClose)這三個消息產生之前不可對此對話框以外的對話框進行操作,當上面3個消息產生后系統負責刪除模態對話框資源。而非模態對話框可以進行其他操作,但必須在三個消息發生后自己在析構函數里回收此對話框資源。

如word文檔中的「查找」對話框就是一個非模態對話框,還可以從文檔中複製文本,然後粘貼到「查找」對話框中,操作比較靈活、方便。當然模態對話框也有其存在的必要,例如一些警告對話框,就是必須要用戶做出選擇后才可以進行其它操作。

Advertisements

Windows是一種面向對象的體系結構,Windows環境和應用程序都是通過消息來交互的,由用戶發出消息,系統響應處理。Windows應用程序開始執行后,Windows為該程序創建一個"消息隊列(message queue)"的數據結構體,用以存放郵寄給該程序可能創建的各種不同窗口的消息。PostMessage()不等待該消息處理完就返回,SendMessage()則必須等待該消息處理完後方可返回(不進隊消息)。

Windows 應用程序創建的每個窗口都在系統核心註冊一個相應的窗口函數,窗口函數程序代碼形式上是一個巨大的switch 語句,用以處理由消息循環發送到該窗口的消息,窗口函數由Windows 採用消息驅動的形式直接調用,而不是由應用程序顯示調用的,窗口函數處理完消息后又將控制權返回給Windows。

Advertisements

非模態對話框是響應一個消息,系統處理一個消息,處理完畢后返回控制權給Windows。

模態對話框在對話框創建后,掛起外部的消息,只是響應對話框內部的消息,而外部消息則全部"過濾"掉了,直到系統接收到WM_DESTROY或WM_CLOSE后,系統返回控制權給模態對話框創建前的線程,繼續模態對話框創建前的線程將執行的代碼。

模態對話框的DoModal()方法可以產生、顯示、銷毀窗口。

非模態對話框需要調用Create()創建,調用ShowWindow進行顯示,最後用delete銷毀。

一、模態對話框(model dialog box)

在程序運行的過程中,若出現了模態對話框,那麼主窗口將無法發送消息,直到模態對話框退出才可以發送。點擊模態對話框中的OK按鈕,模態對話框會被銷毀。

創建一個模態對話框的代碼:

CTestDialog td;

td.DoModal();

其中CTestDialog為所新建和一個對話框資源相關聯的對話框類。

可以創建一個模態對話框類變數,不用擔心它會隨著所在函數返回而被銷毀。因為DoModal()函數的一個功能是,當前只能運行此模態對話框,且停止主窗口的運行,直到模態對話框退出,才允許主窗口運行。

DoModal()函數也有顯示對話框的功能,所以也無需調用其他函數來顯示對話框。

二、非模態對話框(modaless dialog box)

在程序運行的過程中,若出現了非模態對話框,主窗口還可以發送消息。

點擊非模態對話框中的OK按鈕,非模態對話框沒有銷毀,只是隱藏了。若想點擊OK按鈕時,非模態對話框也銷毀,那麼CTestDialog類必須重載其基類CDialog的虛函數OnOK(),在此函數里調用DestroyWindow()來銷毀此對話框。

若和上面一樣的方式創建一個非模態對話框:

CTestDialog td;

td.Create(IDD_DIALOG1); //創建一個非模態對話框

td.ShowWindow(SW_SHOWNORMAL); //顯示非模態對話框

那麼,在運行時,你會發現此對話框無法顯示。這是因為你聲明的對話框變數td是局部變數,但這個函數返回時,td也被析構了,所以無法顯示此對話框。

創建非模態對話框,必須聲明一個指向CTestDialog類的指針變數,且需要顯示地調用ShowWindow()才能將對話框顯示出來。有兩種創建方法:

(1)採用局部變數創建一個非模態對話框

CTestDialog *pTD = new CTestDialog();

pTD->Create(IDD_DIALOG1); //創建一個非模態對話框

pTD->ShowWindow(SW_SHOWNORMAL); //顯示非模態對話框

指針pTD本身存放在棧中,指針pTD指向的對象*pTD存放在堆中。*pTD只有整個應用程序關閉后才會被銷毀,所以可以正常顯示對話框。

這種方法雖然不影響程序的運行,可是指針pTD所指向的內存卻導致不可用,這樣的編程很不好。

(2)採用成員變數創建一個非模態對話框

首先在你所要編寫的類的頭文件中聲明一個指針變數:

private:

CTestDialog *pTD;

然後再在相應的CPP文件,在你要創建對話框的位置添加如下代碼:

//採用成員變數創建一個非模態對話框

pTD = new CTestDialog(); //給指針分配內存

pTD->Create(IDD_DIALOG1); //創建一個非模態對話框

pTD->ShowWindow(SW_SHOWNORMAL); //顯示非模態對話框

指針pTD本身存放在堆中,指針pTD指向的對象*pTD也存放在堆中。

最後在所在類的析構函數中收回pTD所指向的內存:

delete pTD;

三、內存分配的區別

模態對話框定義的定義的局部變數,是在棧上分配的內存,程序一出函數,他的生命周期就終止了,也就被自動釋放。

非模態對話框定義的指針,是在堆上分配的內存 。

在這裡要解釋一下程序在內存中的分佈情況。

1 代碼區

存放函數體的二進位代碼。

2 全局區或靜態區

程序結束後由系統釋放。

2.1 數據區:初始化的全局和靜態局部變數

2.2 BSS:未初始化的全局和靜態局部變數

3 動態數據區

3.1 堆(heap)

以動態的方式分配內存,由程序員分配(在C中用malloc函數,在C++中用new運算符)和釋放(free函數和delete去處符),若程序員不釋放,程序結束時可能由操作系統回收。從低地址到高地址擴散。分配方式類似於數據結構的鏈表。(系統是用鏈表存儲空閑內在地址,是不連續的內存區域,所以堆獲得的空間比較靈活,也比較大)

3.2 棧(stack)

存放局部變數、函數參數和返回值。由編譯器自動分配和釋放。從高地址到低地址擴散(能從棧獲得的空間較小)。棧區也稱為動態數據區。操作方式類似於數據結構中的棧。

4 文字常量區

用於存放常量字元串,程序結束後由系統釋放。

命令行參數與環境區:命令行參數和環境變數。

BSS和數據區統稱為 全局區或靜態區。

全局變數放在全局區(靜態區),函數內部變數stattic in ncount也是放在全局區(靜態區)。函數內部變數char *p = "AAA",p保存的位置在棧區,但p指向的空間位置在全局區(靜態區);函數內部變數char *p = new char,p保存的位置在棧區,但p指向的空間位置卻是在堆區。

低端內存區域→……動態數據區……代碼區、靜態數據區……←高端內存區域;

成員變數和局部變數的區別:

I 在類中位置不同:成員變數在類中方法外;局部變數在方法定義中或者方法聲明上。

II 在內存中的位置不同:成員變數在堆內存;局部變數在棧內存。

III 生命周期不同:成員變數隨著對象的創建而存在,隨著對象的消失而消失。 局部變數隨著方法的調用而存在,隨著方法的調用完畢而消失。

IV 初始化值不同:成員變數有默認值初始化;局部變數沒有默認值初始化,必須定義,賦值,然後才能使用。

V 局部變數名稱可以和成員變數名稱一樣,在方法中使用的時候,採用的是就近原則。

-End-

Advertisements

你可能會喜歡