考慮單窗口狀況:
假設本身經過new建立了一個窗口對象pWnd,而後pWnd->Create。則銷燬窗口的調用次序:
1. 手工調用pWnd->DestroyWindow();
2. DestroyWindow會發送WM_DESTROY;
3. WM_DESTROY對應的消息處理函數是OnDestroy();
4. DestroyWindow會發送WM_NCDESTROY;
5. WM_NCDESTROY對應的消息處理函數是OnNcDestroy;
6. OnNcDestroy最後會調用PostNcDestroy;
7. PostNcDestroy常常被用戶重載以提供釋放內存操做。例如可使用delete this;
經過這種方式,窗口對象對應的窗口和窗口對象自己都被釋放了。
若是含有子窗口:
若是含有子窗口,則調用父窗口的DestroyWindow時,它會向子窗口發送WM_DESTROY和WM_NCDESTROY消息。
具體調用順序參考下文的例子。
DestroyWindow對delete的影響:
應該說前者對後者並無什麼影響。但常常在DestroyWindow間接致使執行的PostNcDestroy中delete窗口對象指針,即delete this。
CView::PostNcDestroy中惟一的操做就是delete this;CframeWnd::PostNcDestory也是如此。而默認的CWnd::PostNcDestroy是空操做,CDialog中也沒有對其進行重載,即也是空。
delete對Destroy的影響:
delete會致使析構函數。CWnd的析構函數中有對DestroyWindow的調用,但必須保證:
m_hWnd != NULL &&
this != (CWnd*) &wndTop &&this != (CWnd*)&wndBottom &&
this != (CWnd*)&wndTopMost &&this != (CWnd*)&wndNoTopMost。
Cdialog的析構函數中也有對DestroyWindow的調用,但條件比較鬆,只須要m_hWnd != NULL。另外Cdialog::DoModal也會調用DestroyWindow。
CFrameWnd的OnClose中會調用DestroyWindow,但其析構中不會調用DestroyWindow。
CView的析構也不會調用DestroyWindow。
一個SDI程序的銷燬過程
有CMainFrame類、CMyView類。而且CMyView有兩個子窗口CMyDlg和CmyWnd的實例。
點擊退出按鈕,CMainFrame會收到WM_CLOSE消息。CframeWnd(CMainFrame的父類)間接會調用CWnd::DestroyWindow;它首先向CMyView發送WM_DESTORY和WM_NCDESTROY消息,並引起相應的處理函數;而後向CMyDlg發送WM_DESTORY和WM_NCDESTROY消息,並引起相應的處理函數;而後向CMyWnd發送WM_DESTORY和WM_NCDESTROY消息,並引起相應的處理函數。
具體的執行順序是:
1. 調用CMainFrame::DestroyWindow
2. CFrameWnd::OnDestroy
3. CMyView::OnDestroy
4. CmyWnd::OnDestroy
5. CmyDlg::OnDestroy
6. CmyWnd::PostNcDestroy
7. CmyWnd的析構
8. CmyDlg::OnDestroy
9. CmyDlg的析構
10. CMyView::PostNcDestroy
11. CmyView的析構
12. CMainFrame的析構
13. CMainFrame::DestroyWindow退出
上面狀況是假設咱們在CmyWnd和CmyDlg的PostNcDestroy中添加了delete this。若是沒有添加,則7,10不會執行。
由於CView::PostNcDestroy中調用了delete this,因此而後會執行CMyView的析構操做。由於CframeWnd::PostNcDestroy中調用了delete this,因此最後執行CMainFrame的析構操做。
若是本身的CmyDlg和CmyWnd在PostNcDestroy中有delete this;則兩者會被析構。不然內存泄漏。固然delete也能夠放在CMyView的析構中作,只是不夠OO。
總結
能夠有兩種方法銷燬窗口對象對應的窗口和釋放窗口對象指針。一種是經過DestroyWindow。這是比較好的方法,由於最後MFC會自動相應WM_CLOSE致使CframWnd::DestroyWindow被調用,而後會一次釋放全部子窗口的句柄。用戶須要作的是在PostNcDestroy中釋放堆窗口對象指針。但由於某些對象是在棧中申請的,因此delete this可能出錯。這就要保證寫程序時本身建立的窗口儘可能使用堆申請。
另外一種是delete。Delete一個窗口對象指針有的窗口類(如CWnd,Cdialog)會間接調用DestroyWindow,有的窗口類(如CView,CframeWn)不會調用DestroyWindow。因此要當心應對。
兩者是相互調用的,很繁瑣。
一段很好的文章:(做者:聞怡洋)
一個MFC窗口對象包括兩方面的內容:一是窗口對象封裝的窗口,即存放在m_hWnd成員中的HWND(窗口句柄),二是窗口對象自己是一個C++對象。要刪除一個MFC窗口對象,應該先刪除窗口對象封裝的窗口,而後刪除窗口對象自己。
刪除窗口最直接方法是調用CWnd::DestroyWindow或::DestroyWindow,前者封裝了後者的功能。前者不只會調用後者,並且會使成員m_hWnd保存的HWND無效(NULL)。若是DestroyWindow刪除的是一個父窗口或擁有者窗口,則該函數會先自動刪除全部的子窗口或被擁有者,而後再刪除父窗口或擁有者。在通常狀況下,在程序中沒必要直接調用DestroyWindow來刪除窗口,由於MFC會自動調用DestroyWindow來刪除窗口。例如,當用戶退出應用程序時,會產生WM_CLOSE消息,該消息會致使MFC自動調用CWnd::DestroyWindow來刪除主框架窗口,當用戶在對話框內按了OK或Cancel按鈕時,MFC會自動調用CWnd::DestroyWindow來刪除對話框及其控件。
窗口對象自己的刪除則根據對象建立方式的不一樣,分爲兩種狀況。在MFC編程中,會使用大量的窗口對象,有些窗口對象以變量的形式嵌入在別的對象內或以局部變量的形式建立在堆棧上,有些則用new操做符建立在堆中。對於一個以變量形式建立的窗口對象,程序員沒必要關心它的刪除問題,由於該對象的生命期老是有限的,若該對象是某個對象的成員變量,它會隨着父對象的消失而消失,若該對象是一個局部變量,那麼它會在函數返回時被清除。
對於一個在堆中動態建立的窗口對象,其生命期倒是任意長的。初學者在學習C++編程時,對new操做符的使用每每不太踏實,由於用new在堆中建立對象,就不能忘記用delete刪除對象。讀者在學習MFC的例程時,可能會產生這樣的疑問,爲何有些程序用new建立了一個窗口對象,卻未顯式的用delete來刪除它呢?問題的答案就是有些MFC窗口對象具備自動清除的功能。
如前面講述非模態對話框時所提到的,當調用CWnd::DestroyWindow或::DestroyWindow刪除一個窗口時,被刪除窗口的PostNcDestroy成員函數會被調用。缺省的PostNcDestroy什麼也不幹,但有些MFC窗口類會覆蓋該函數並在新版本的PostNcDestroy中調用delete this來刪除對象,從而具備了自動清除的功能。此類窗口對象一般是用new操做符建立在堆中的,但程序員沒必要操心用delete操做符去刪除它們,由於一旦調用DestroyWindow刪除窗口,對應的窗口對象也會緊接着被刪除。
不具備自動清除功能的窗口類以下所示。這些窗口對象一般是以變量的形式建立的,無需自動清除功能。
全部標準的Windows控件類。
1. 從CWnd類直接派生出來的子窗口對象(如用戶定製的控件)。
2. 切分窗口類CSplitterWnd。
3. 缺省的控制條類(包括工具條、狀態條和對話條)。
4. 模態對話框類。
具備自動清除功能的窗口類以下所示,這些窗口對象一般是在堆中建立的。
1. 主框架窗口類(直接或間接從CFrameWnd類派生)。
2. 視圖類(直接或間接從CView類派生)。
讀者在設計本身的派生窗口類時,可根據窗口對象的建立方法來決定是否將窗口類設計成能夠自動清除的。例如,對於一個非模態對話框來講,其對象是建立在堆中的,所以應該具備自動清除功能。
綜上所述,對於MFC窗口類及其派生類來講,在程序中通常沒必要顯式刪除窗口對象。也就是說,既沒必要調用DestroyWindow來刪除窗口對象封裝的窗口,也沒必要顯式地用delete操做符來刪除窗口對象自己。只要保證非自動清除的窗口對象是以變量的形式建立的,自動清除的窗口對象是在堆中建立的,MFC的運行機制就能夠保證窗口對象的完全刪除。
若是須要手工刪除窗口對象,則應該先調用相應的函數(如CWnd::DestroyWindow)刪除窗口,而後再刪除窗口對象.對於以變量形式建立的窗口對象,窗口對象的刪除是框架自動完成的.對於在堆中動態建立了的非自動清除的窗口對象,必須在窗口被刪除後,顯式地調用delete來刪除對象(通常在擁有者或父窗口的析構函數中進行).對於具備自動清除功能的窗口對象,只需調用CWnd::DestroyWindow便可刪除窗口和窗口對象。注意,對於在堆中建立的窗口對象,不要在窗口還未關閉的狀況下就用delete操做符來刪除窗口對象.程序員