TN017:終結視窗物件
此附注描述 方法的使用 CWnd::PostNcDestroy
方式。 如果您想要自訂衍生物件的配置 CWnd
,請使用此方法。 此附注也會說明為何您應該使用 CWnd::DestroyWindow
來終結 C++ Windows 物件,而不是 delete
運算子。
如果您遵循本文中的指導方針,則幾乎沒有清除問題。 這些問題可能是因為忘記刪除/釋放 C++ 記憶體、忘記釋放系統資源之類的 HWND
問題,或釋放物件太多次等問題。
問題
每個 Windows 物件(衍生自 CWnd
的類別物件)都代表 C++ 物件和 HWND
。 C++ 物件會配置在應用程式的堆積中,而 HWND
是由視窗管理員在系統資源中配置。 由於有數種方式可以終結視窗物件,因此我們必須提供一組規則,以防止系統資源或記憶體流失。 這些規則也必須防止物件和 Windows 控制碼被終結一次以上。
終結視窗
以下是終結 Windows 物件的兩種允許方式:
呼叫
CWnd::DestroyWindow
或 Windows APIDestroyWindow
。使用
delete
運算子明確刪除。
第一個案例是迄今為止最常見的案例。 即使您的程式碼未直接呼叫 DestroyWindow
,此案例仍適用。 當使用者直接關閉框架視窗時,此動作會產生WM_CLOSE訊息,而此訊息的預設回應是呼叫 DestroyWindow
。 當父視窗終結時,Windows 會 DestroyWindow
呼叫其所有子系。
第二種情況,在 delete
Windows 物件上使用 運算子應該很少見。 以下是使用 delete
是正確選擇的一些案例。
使用 自動清除 CWnd::PostNcDestroy
當系統終結 Windows 視窗時,傳送至視窗的最後一則 Windows 訊息為 WM_NCDESTROY
。 該訊息的預設 CWnd
處理常式為 CWnd::OnNcDestroy
。 OnNcDestroy
會從 C++ 物件中斷連結 HWND
,並呼叫虛擬函式 PostNcDestroy
。 某些類別會覆寫此函式以刪除 C++ 物件。
的預設實 CWnd::PostNcDestroy
作不會執行任何動作,這適用于堆疊框架或內嵌在其他物件上配置的視窗物件。 此行為不適用於針對堆積上配置而設計的視窗物件,而不需要任何其他物件。 換句話說,它不適用於未內嵌在其他 C++ 物件中的視窗物件。
專為在堆積上單獨配置而設計的類別會覆寫 PostNcDestroy
方法來執行 delete this;
。 此語句會釋放任何與 C++ 物件相關聯的記憶體。 即使預設 CWnd
解構函式會呼叫 DestroyWindow
如果 m_hWnd
不是 NULL
,此呼叫並不會導致無限遞迴,因為控制碼將會中斷連結,並在 NULL
清除階段期間。
注意
系統通常會在處理 Windows WM_NCDESTROY
訊息和 HWND
和 C++ 視窗物件之後呼叫 CWnd::PostNcDestroy
,不再連線。 如果發生失敗,系統也會在大部分 CWnd::Create
呼叫的實作中呼叫 CWnd::PostNcDestroy
。 本文稍後會說明自動清除規則。
自動清除類別
下列類別不是針對自動清除所設計。 它們通常內嵌在其他 C++ 物件或堆疊上:
所有標準 Windows 控制項(
CStatic
、、CListBox
CEdit
等等)。任何衍生自
CWnd
的子視窗(例如自訂控制項)。分隔器視窗 (
CSplitterWnd
)。預設控制列(衍生自
CControlBar
的類別,請參閱 技術附注 31 ,以啟用控制項列物件的自動刪除)。針對堆疊框架上的強制回應對話所設計的對話方塊 。
CDialog
。除了 以外的所有標準對話方塊
CFindReplaceDialog
。ClassWizard 所建立的預設對話方塊。
下列類別是專為自動清除所設計。 它們通常會由自己在堆積上配置:
主框架視窗(直接或間接衍生自
CFrameWnd
)。檢視視窗(直接或間接衍生自
CView
)。
如果您想要中斷這些規則,您必須覆寫 PostNcDestroy
衍生類別中的 方法。 若要將自動清除新增至類別,請呼叫基類,然後執行 delete this;
。 若要從類別移除自動清除,請直接呼叫 CWnd::PostNcDestroy
,而不是 PostNcDestroy
直接基類的 方法。
變更自動清除行為的最常見用法是建立可在堆積上配置的無強制回應對話方塊。
呼叫時機 delete
建議您呼叫 DestroyWindow
來終結 Windows 物件,也就是 C++ 方法或全域 DestroyWindow
API。
請勿呼叫全域 DestroyWindow
API 來終結 MDI 子視窗。 您應該改用虛擬方法 CWnd::DestroyWindow
。
對於未執行自動清除的 C++ Window 物件,當您嘗試在解構函 VTBL
式中 CWnd::~CWnd
呼叫 DestroyWindow
時,如果 不是指向正確衍生類別,則使用 delete
運算子可能會導致記憶體流失。 發生洩漏的原因是系統找不到要呼叫的適當 destroy 方法。 使用 DestroyWindow
而不是 delete
避免這些問題。 因為這個錯誤可能很微妙,因此如果您面臨風險,在偵錯模式中編譯會產生下列警告。
Warning: calling DestroyWindow in CWnd::~CWnd
OnDestroy or PostNcDestroy in derived class will not be called
對於執行自動清除的 C++ Windows 物件,您必須呼叫 DestroyWindow
。 如果您直接使用 delete
運算子,MFC 診斷記憶體配置器會通知您釋放記憶體兩次。 這兩個出現次數是您在 的自動清除實 PostNcDestroy
作中第一次明確呼叫和 間接呼叫 delete this;
。
在呼叫 DestroyWindow
非自動清除物件之後,C++ 物件仍會四處走動,但 m_hWnd
會是 NULL
。 在自動清除物件上呼叫 DestroyWindow
之後,C++ 物件將會消失,由 C++ 刪除運算子在 的自動清除實 PostNcDestroy
作中釋出。