首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> VC >> 關於VC++

MFC 模態與非模態對話方塊銷毀機制差異深度解析與正確姿勢

     編輯:關於VC++

MFC 對話方塊銷毀機制在模態 (DoModal) 與非模態 (Create) 間存在本質差異,錯誤用法會導致記憶體洩漏、Debug 斷言失敗甚至崩潰。本文系統梳理銷毀流程、正確重寫點與自動清理模式。

模態對話方塊銷毀流程


// 使用者點擊「確定」
OnOK() → UpdateData(TRUE) → EndDialog(IDOK) → DestroyWindow() → OnDestroy() → PostNcDestroy()

// 使用者點擊「取消」/Esc
OnCancel() → EndDialog(IDCANCEL) → DestroyWindow() → OnDestroy() → PostNcDestroy()

// 關閉按鈕 (系統選單)
OnClose() → OnCancel() → ... (同上)

// 關鍵等價關係
EndDialog(IDOK)   ≡ OnOK()   (含 UpdateData)
EndDialog(IDCANCEL) ≡ OnCancel() ≡ OnClose() (不含 UpdateData)

重寫原則

  • 必須呼叫基類對應函數:CDialog::OnOK()、CDialog::OnCancel()
  • 資料驗證失敗時在 OnOK 中 return 阻止關閉
  • 清理資源放在 OnDestroy 或 PostNcDestroy

非模態對話方塊銷毀流程 (堆疊建立)


// 建立
CMyDlg* pDlg = new CMyDlg;
pDlg->Create(IDD_MYDLG, this);
pDlg->ShowWindow(SW_SHOW);

// 關閉按鈕處理 (必須重寫,不可用基類預設)
void CMyDlg::OnOK() { DestroyWindow(); }      // 不呼叫 CDialog::OnOK()!
void CMyDlg::OnCancel() { DestroyWindow(); }  // 不呼叫 CDialog::OnCancel()!
void CMyDlg::OnClose() { DestroyWindow(); }   // 同樣

// 銷毀鏈
DestroyWindow() → OnDestroy() → PostNcDestroy()

關鍵差異總表

特性模態非模態 (堆疊建立)
建立方式DoModal() (棧上/內部管理)new + Create() (堆上/手動管理)
關閉觸發EndDialogDestroyWindow
OnOK/OnCancel 預設呼叫 EndDialog**不關閉對話方塊** (需重寫呼叫 DestroyWindow)
物件銷毀責任框架自動**程式設計師負責 delete**
自動清理模式不需要PostNcDestroy 中 delete this

非模態自動清理標準模式 (Microsoft 官方推薦)


class CMyModelessDlg : public CDialog {
    // ...
    virtual void PostNcDestroy() {
        CDialog::PostNcDestroy();  // 基類清理
        delete this;               // 自我刪除 (僅限 new 建立的物件!)
    }
};

// 使用端
void CMainDlg::OnShowModeless() {
    CMyModelessDlg* pDlg = new CMyModelessDlg;
    pDlg->Create(IDD_MODELESS, this);
    pDlg->ShowWindow(SW_SHOW);
    // 無需手動 delete,關閉時自動清理
}

Debug 斷言警告根因

Warning: calling DestroyWindow in CWnd::~CWnd OnDestroy or PostNcDestroy in derived class will not be called

原因:在 CWnd 解構函數中呼叫 DestroyWindow,導致虛函數機制失效,OnDestroy/PostNcDestroy 不被呼叫。
解決:非模態對話方塊**絕不**在解構函數中呼叫 DestroyWindow,改用 PostNcDestroy 自刪模式。

最佳實踐檢查清單

  1. 模態:重寫 OnOK/OnCancel → 呼叫基類 → 清理資源放 OnDestroy
  2. 非模態:重寫 OnOK/OnCancel/OnClose → 呼叫 DestroyWindow → 實作 PostNcDestroy { CDialog::PostNcDestroy(); delete this; }
  3. 非模態指標變數設為 NULL 後不可再存取
  4. 主視窗關閉時遍歷並關閉所有非模態對話方塊
  5. 使用智慧指標 std::unique_ptr 配合自訂刪除器管理非模態對話方塊生命週期 (現代 C++)
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved