無模式對話框 (CDialog::Create) 與模態對話框 (DoModal) 生命週期管理迥異。模態對話框由框架自動銷毀,無模式對話框則需開發者手動管理物件銷毀時機。錯誤的管理易導致記憶體洩漏、存取違規或資源未釋放。本文透過完整範例剖析兩種主流銷毀策略。
void CMyView::OnOpenDlg()
{
CTestDlg* dlg = new CTestDlg; // 1. 堆疊配置物件
dlg->Create(IDD_TEST_DLG, NULL); // 2. 建立視窗 (非模態)
dlg->ShowWindow(SW_SHOW); // 3. 顯示視窗
// 此處不可 delete dlg!視窗仍在顯示中
}
關鍵:物件必須在堆疊上配置 (new),且不在建立函式中刪除。
在對話框類別內覆寫 OnOK、OnCancel、OnDestroy:
void CTestDlg1::OnOK()
{
CDialog::OnOK();
DestroyWindow(); // 觸發 WM_DESTROY
}
void CTestDlg1::OnCancel()
{
CDialog::OnCancel();
DestroyWindow();
}
void CTestDlg1::OnDestroy()
{
CDialog::OnDestroy();
AfxMessageBox(_T("call delete this"));
delete this; // 關鍵:視窗銷毀時刪除自身物件
}
優點:封裝性好,呼叫端無需追蹤指標。
缺點:delete this 需極度小心,確保之後不再存取任何成員。
對話框建立時傳遞父視窗指標,銷毀時發送自訂訊息通知父視窗刪除物件。
CTestDlg2::CTestDlg2(CWnd* pParent)
: CDialog(IDD, pParent), m_pParent(pParent)
{
ASSERT(pParent); // 父視窗指標必須有效且非暫時性
}
void CTestDlg2::OnOK() { CDialog::OnOK(); DestroyWindow(); }
void CTestDlg2::OnCancel() { CDialog::OnCancel(); DestroyWindow(); }
void CTestDlg2::OnDestroy()
{
CDialog::OnDestroy();
// 發送自訂訊息給父視窗,傳遞自身指標
m_pParent->PostMessage(WM_DELETE_DLG, (WPARAM)this);
}
// 標頭檔
afx_msg LONG OnDelDlgMsg(WPARAM wParam, LPARAM lParam);
// 訊息映射
ON_MESSAGE(WM_DELETE_DLG, OnDelDlgMsg)
// 實作
LONG CMyView::OnDelDlgMsg(WPARAM wParam, LPARAM lParam)
{
delete (CTestDlg2*)wParam; // 刪除對話框物件
return 0;
}
// 建立對話框
void CMyView::OnTest2()
{
CTestDlg2* dlg = new CTestDlg2(this); // 傳遞 this 作為父視窗
dlg->Create(IDD_TEST_DLG_2);
dlg->ShowWindow(SW_SHOW);
}
優點:物件擁有權清晰歸屬父視窗,避免 delete this 風險。
缺點:父視窗需定義訊息處理,略增複雜度。
利用訊息機制將使用者輸入即時傳遞給父視窗:
// 對話框中按鈕處理
void CTestDlg2::OnCommBtn()
{
char szOut[30];
GetDlgItemText(IDC_OUT, szOut, 30);
m_pParent->SendMessage(WM_DLG_NOTIFY, (WPARAM)szOut);
}
// 父視窗訊息映射
ON_MESSAGE(WM_DLG_NOTIFY, OnDlgNotifyMsg)
// 父視窗處理
LONG CMyView::OnDlgNotifyMsg(WPARAM wParam, LPARAM lParam)
{
m_szOut = (char*)wParam; // 保存字串指標 (注意生命週期)
Invalidate(); // 觸發重繪
return 0;
}
// OnDraw 中顯示
void CMyView::OnDraw(CDC* pDC)
{
pDC->TextOut(0, 0, _T("Display String"));
pDC->TextOut(0, 20, m_szOut);
}
注意:以 SendMessage 同步傳遞指標需確保緩衝區在對話框銷毀前有效,或改用 PostMessage 配合字串複製。
| 錯誤 | 後果 | 正確做法 |
|---|---|---|
| 棧上配置對話框物件 | 函式返回物件銷毀,視窗句柄失效 | 使用 new 堆疊配置 |
建立後未呼叫 ShowWindow | 視窗不可見 | 必須呼叫 ShowWindow(SW_SHOW) |
在建立函式中 delete | 視窗尚未顯示即銷毀 | 在 OnDestroy 或父視窗訊息中刪除 |
delete this 後繼續存取成員 | 未定義行為/崩潰 | delete this 必須是最後一行執行碼 |
| 父視窗指標為暫時物件 | 發送訊息時指標失效 | 父視窗必須為長生命週期物件 (如 View、Frame) |
無模式對話框賦予介面極大靈活性,但要求開發者精確掌控物件生命週期。採用「自我銷毀」或「父視窗管理」兩種經典模式,配合訊息機制實現資料雙向流動,是 MFC 開發的核心技能之一。