MFC中對話方塊有兩種形式, 一個是模態對話方塊(model dialog box), 一個是非模態對話方塊(modeless dialog box)。
模態對話方塊是一種阻塞式的對話方塊, 即沒有處理完該對話方塊, 不能對其他地方進行操作。 非模態對話方塊和模態對話方塊相反, 它不是阻塞型的, 即你可以同時操作其他的視窗, 比如父視窗。
模態對話方塊在關閉對話方塊(OnOk、OnCancel、OnClose)這三個消息產生之前不可對此對話方塊以外的對話方塊進行操作, 當上面3個消息產生後系統負責刪除模態對話方塊資源。 而非模態對話方塊可以進行其他操作, 但必須在三個消息發生後自己在析構函數裡回收此對話方塊資源。
如word文檔中的“查找”對話方塊就是一個非模態對話方塊, 還可以從文檔中複製文本, 然後粘貼到“查找”對話方塊中, 操作比較靈活、方便。 當然模態對話方塊也有其存在的必要, 例如一些警告對話方塊, 就是必須要用戶做出選擇後才可以進行其它操作。
Windows是一種物件導向的體系結構, Windows環境和應用程式都是通過消息來交互的, 由使用者發出消息, 系統回應處理。 Windows應用程式開始執行後, Windows為該程式創建一個"訊息佇列(message queue)"的資料結構體, 用以存放郵寄給該程式可能創建的各種不同視窗的消息。 PostMessage()不等待該消息處理完就返回, SendMessage()則必須等待該消息處理完後方可返回(不進隊消息)。
Windows 應用程式創建的每個視窗都在系統核心註冊一個相應的視窗函數,
非模態對話方塊是回應一個消息, 系統處理一個消息, 處理完畢後返回控制權給Windows。
模態對話方塊在對話方塊創建後, 掛起外部的消息, 只是回應對話方塊內部的消息, 而外部消息則全部"過濾"掉了, 直到系統接收到WM_DESTROY或WM_CLOSE後, 系統返回控制權給模態對話方塊創建前的執行緒, 繼續模態對話方塊創建前的執行緒將執行的代碼。
模態對話方塊的DoModal()方法可以產生、顯示、銷毀視窗。
非模態對話方塊需要調用Create()創建, 調用ShowWindow進行顯示, 最後用delete銷毀。
一、模態對話方塊(model dialog box)在程式運行的過程中, 若出現了模態對話方塊, 那麼主視窗將無法發送消息, 直到模態對話方塊退出才可以發送。 點擊模態對話方塊中的OK按鈕, 模態對話方塊會被銷毀。
創建一個模態對話方塊的代碼:
CTestDialog td;
td.DoModal();
其中CTestDialog為所新建和一個對話方塊資源相關聯的對話方塊類。
可以創建一個模態對話方塊類變數, 不用擔心它會隨著所在函數返回而被銷毀。 因為DoModal()函數的一個功能是, 當前只能運行此模態對話方塊, 且停止主視窗的運行, 直到模態對話方塊退出, 才允許主視窗運行。
DoModal()函數也有顯示對話方塊的功能, 所以也無需調用其他函數來顯示對話方塊。
在程式運行的過程中, 若出現了非模態對話方塊, 主視窗還可以發送消息。
點擊非模態對話方塊中的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-