專案中使用非模態對話框,Debug 版本輸出警告:
Warning: calling DestroyWindow in CWnd::~CWnd OnDestroy or PostNcDestroy in derived class will not be called
表面是 Warning,實則隱含嚴重資源洩漏風險。本文系統梳理兩類對話框的銷毀流程、正確重寫姿勢、自動清理機制,並解答常見誤區。
// 使用者點擊 [確定] 按鈕 (IDOK)
OnOK()
→ UpdateData(TRUE) // 將控制項資料同步到成員變數
→ EndDialog(IDOK)
→ DestroyWindow()
→ OnDestroy()
→ PostNcDestroy() // 此處預設不刪除 this(棧上物件)
// 使用者點擊 [取消] / 關閉按鈕 (IDCANCEL)
OnCancel()
→ EndDialog(IDCANCEL) // 無 UpdateData
→ DestroyWindow()
→ OnDestroy()
→ PostNcDestroy()
Close() ≡ OnCancel() ≡ EndDialog(IDCANCEL)OnOK() ≡ EndDialog(IDOK)(差別在於是否 UpdateData)OnOK()/OnCancel()/OnClose(),**必須呼叫基類版本**DoModal() 內部建構),**無需手動 delete**| 屬性 | 模態 | 非模態 |
|---|---|---|
| 建立 | dlg.DoModal() | new CMyDlg; dlg->Create(...); dlg->ShowWindow(SW_SHOW); |
| 儲存位置 | 棧 | 堆 |
| 生命週期 | DoModal 返回即結束 | 需手動管理,**必須 delete** |
// MyModelessDlg.h
class CMyModelessDlg : public CDialog {
DECLARE_DYNCREATE(CMyModelessDlg)
public:
afx_msg void OnOK(); // 重寫
afx_msg void OnCancel(); // 重寫
virtual void PostNcDestroy(); // 關鍵:自動清理
};
// MyModelessDlg.cpp
void CMyModelessDlg::OnOK() {
// 非模態:直接銷毀視窗,不要呼叫基類 OnOK()(會呼叫 EndDialog)
DestroyWindow();
}
void CMyModelessDlg::OnCancel() {
// 同理
DestroyWindow();
}
void CMyModelessDlg::PostNcDestroy() {
CDialog::PostNcDestroy(); // 基類清理
delete this; // 【核心】自動刪除堆上物件
}
DestroyWindow() 發送 WM_DESTROY → OnDestroy() → WM_NCDESTROY → PostNcDestroy()PostNcDestroy() 是**視窗物件完全銷毀前的最後一個虛函式呼叫**delete this**| 錯誤做法 | 後果 | 正確做法 |
|---|---|---|
非模態 OnOK() 呼叫 CDialog::OnOK() | 觸發 EndDialog,模態邏輯錯亂,資源洩漏 | 呼叫 DestroyWindow() |
非模態 OnCancel() 呼叫 CDialog::OnCancel() | 同左 | 呼叫 DestroyWindow() |
在呼叫端 delete pDlg 而非 PostNcDestroy | 視窗尚未銷毀即刪除物件,導致存取已釋放記憶體 | 讓 MFC 視窗銷毀流程完成後自動清理 |
忘記重寫 PostNcDestroy | 堆上物件永不釋放,**記憶體洩漏** | 必須重寫並 delete this |
非模態對話框:
new CMyDlg() → 建構函式
→ Create() → 建立視窗句柄
→ ShowWindow(SW_SHOW)
→ ... 使用者操作 ...
→ 點擊關閉/確定/取消
→ OnOK()/OnCancel() (重寫版)
→ DestroyWindow()
→ OnDestroy()
→ PostNcDestroy() (重寫版)
→ CDialog::PostNcDestroy()
→ delete this ← 物件生命結束
CDialog::PostNcDestroy —— 英文原版說明,建議對照閱讀。
OnOK/OnCancel/OnClose 時**必呼基類**OnOK/OnCancel 僅呼叫 DestroyWindow()PostNcDestroy 並 delete this**new,**不要**用棧上物件DestroyWindow in ~CWnd 警告本文整理自原博客技術筆記,經結構化重寫發布