分享方式:


TN017:終結視窗物件

此附注描述 方法的使用 CWnd::PostNcDestroy 方式。 如果您想要自訂衍生物件的配置 CWnd ,請使用此方法。 此附注也會說明為何您應該使用 CWnd::DestroyWindow 來終結 C++ Windows 物件,而不是 delete 運算子。

如果您遵循本文中的指導方針,則幾乎沒有清除問題。 這些問題可能是因為忘記刪除/釋放 C++ 記憶體、忘記釋放系統資源之類的 HWND 問題,或釋放物件太多次等問題。

問題

每個 Windows 物件(衍生自 CWnd 的類別物件)都代表 C++ 物件和 HWND 。 C++ 物件會配置在應用程式的堆積中,而 HWND 是由視窗管理員在系統資源中配置。 由於有數種方式可以終結視窗物件,因此我們必須提供一組規則,以防止系統資源或記憶體流失。 這些規則也必須防止物件和 Windows 控制碼被終結一次以上。

終結視窗

以下是終結 Windows 物件的兩種允許方式:

  • 呼叫 CWnd::DestroyWindow 或 Windows API DestroyWindow

  • 使用 delete 運算子明確刪除。

第一個案例是迄今為止最常見的案例。 即使您的程式碼未直接呼叫 DestroyWindow ,此案例仍適用。 當使用者直接關閉框架視窗時,此動作會產生WM_CLOSE訊息,而此訊息的預設回應是呼叫 DestroyWindow 。 當父視窗終結時,Windows 會 DestroyWindow 呼叫其所有子系。

第二種情況,在 delete Windows 物件上使用 運算子應該很少見。 以下是使用 delete 是正確選擇的一些案例。

使用 自動清除 CWnd::PostNcDestroy

當系統終結 Windows 視窗時,傳送至視窗的最後一則 Windows 訊息為 WM_NCDESTROY 。 該訊息的預設 CWnd 處理常式為 CWnd::OnNcDestroyOnNcDestroy 會從 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 作中釋出。

另請參閱

依編號的技術注意事項
依類別分類的技術注意事項