您的位置:首頁>正文

VC|瞭解模態對話方塊與非模態對話方塊的內在機制及記憶體分配差異

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

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

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

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

Windows是一種物件導向的體系結構, Windows環境和應用程式都是通過消息來交互的, 由使用者發出消息, 系統回應處理。 Windows應用程式開始執行後, Windows為該程式創建一個"訊息佇列(message queue)"的資料結構體, 用以存放郵寄給該程式可能創建的各種不同視窗的消息。 PostMessage()不等待該消息處理完就返回, SendMessage()則必須等待該消息處理完後方可返回(不進隊消息)。

Windows 應用程式創建的每個視窗都在系統核心註冊一個相應的視窗函數,

視窗函數程式碼形式上是一個巨大的switch 語句, 用以處理由消息迴圈發送到該視窗的消息, 視窗函數由Windows 採用消息驅動的形式直接調用, 而不是由應用程式顯示調用的, 視窗函數處理完消息後又將控制權返回給Windows。

非模態對話方塊是回應一個消息, 系統處理一個消息, 處理完畢後返回控制權給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-

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-

同類文章
Next Article
喜欢就按个赞吧!!!
点击关闭提示