考慮單視窗情況:
假設自己通過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消息,并引發相應的處理函數。
具體的執行順序是:
調用CMainFrame::DestroyWindow
2. CFrameWnd::OnDestroy
CMyView::OnDestroy
4. CmyWnd::OnDestroy
CmyDlg::OnDestroy
6. CmyWnd::PostNcDestroy
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控件類。
從CWnd類直接派生出來的子視窗對象(如使用者定制的控件)。
2. 切分視窗類CSplitterWnd。
預設的控制條類(包括工具條、狀态條和對話條)。
模态對話框類。
具有自動清除功能的視窗類如下所示,這些視窗對象通常是在堆中建立的。
主架構視窗類(直接或間接從CFrameWnd類派生)。
視圖類(直接或間接從CView類派生)。
讀者在設計自己的派生視窗類時,可根據視窗對象的建立方法來決定是否将視窗類設計成可以自動清除的。例如,對于一個非模态對話框來說,其對象是建立在堆中的,是以應該具有自動清除功能。
綜上所述,對于MFC視窗類及其派生類來說,在程式中一般不必顯式删除視窗對象。也就是說,既不必調用DestroyWindow來删除視窗對象封裝的視窗,也不必顯式地用delete操作符來删除視窗對象本身。隻要保證非自動清除的視窗對象是以變量的形式建立的,自動清除的視窗對象是在堆中建立的,MFC的運作機制就可以保證視窗對象的徹底删除。
如果需要手工删除視窗對象,則應該先調用相應的函數(如CWnd::DestroyWindow)删除視窗,然後再删除視窗對象.對于以變量形式建立的視窗對象,視窗對象的删除是架構自動完成的.對于在堆中動态建立了的非自動清除的視窗對象,必須在視窗被删除後,顯式地調用delete來删除對象(一般在擁有者或父視窗的析構函數中進行).對于具有自動清除功能的視窗對象,隻需調用CWnd::DestroyWindow即可删除視窗和視窗對象。注意,對于在堆中建立的視窗對象,不要在視窗還未關閉的情況下就用delete操作符來删除視窗對象.