MFC の例外処理で CException* を受け取ったとき、Delete() を呼ぶ場面と呼んではいけない場面があります。
この記事では、C++ の catch で捕捉した場合は Delete() が必要、MFC の CATCH マクロで捕捉した場合は自動削除される、という違いを整理します。
まず結論:Delete() が必要なのは C++ catch で受けたとき
| 捕捉方法 | Delete() の扱い | 理由 |
|---|---|---|
catch (CException* e) | 必要 | C++ の catch では MFC 例外オブジェクトが自動削除されない |
CATCH(CFileException, e) | 不要。呼ばない | MFC マクロがブロック終了時に自動削除する |
| 再スローする | その場では呼ばない | 上位の捕捉側に所有権を渡す |
delete e; | 使わない | スタック上やグローバルな例外オブジェクトの可能性がある |
Microsoft のドキュメントでも、MFC マクロで捕捉した場合は例外オブジェクトが自動削除され、C++ の catch キーワードで捕捉した場合は自動削除されない、と説明されています。
なぜ delete ではなく Delete() なのか
CException::Delete() は、例外オブジェクトがヒープ上に作られている場合だけ削除する関数です。MFC の例外オブジェクトは、ヒープ上に作られることも、スタック上やグローバルなオブジェクトとして存在することもあります。
// CException::Delete の概念
void CException::Delete()
{
if (m_bAutoDelete)
{
delete this;
}
}
delete e; を直接呼ぶと、ヒープ上ではないオブジェクトに対して解放してしまう可能性があります。MFC 例外を自分で破棄する必要がある場面では、delete ではなく e->Delete() を使ってください。
C++ catch で捕捉する正しいパターン
C++ 標準の try-catch で MFC 例外を受ける場合は、CException* として捕捉し、処理が終わったら Delete() を呼びます。
try
{
CFile file;
if (!file.Open(strPath, CFile::modeRead))
{
AfxThrowFileException(CFileException::fileNotFound,
-1,
strPath);
}
}
catch (CFileException* e)
{
TCHAR szError[256]{};
e->GetErrorMessage(szError, 256);
AfxMessageBox(szError);
// C++ catch では自動削除されないため必要
e->Delete();
}
MFC CATCH マクロでは Delete() を呼ばない
MFC の TRY / CATCH / END_CATCH マクロを使う場合は、マクロ側が例外オブジェクトを削除します。ここで e->Delete() も呼ぶと、二重に解放することになります。
TRY
{
CFile file;
file.Open(strPath, CFile::modeRead);
}
CATCH(CFileException, e)
{
TCHAR szError[256]{};
e->GetErrorMessage(szError, 256);
AfxMessageBox(szError);
// ここでは e->Delete() を呼ばない。
// CATCH マクロが自動削除する。
}
END_CATCH
TRY / CATCH と C++ 標準の try-catch の違いは、【MFC】try-catch (C++) vs CATCH (MFCマクロ) の違いで比較しています。
よくある間違い
// NG: C++ catch で Delete() を呼んでいない
catch (CException* e)
{
AfxMessageBox(_T("エラーが発生しました"));
// e->Delete() がないためリーク要因になる
}
// NG: delete を直接呼んでいる
catch (CException* e)
{
delete e; // ヒープ上とは限らないため危険
}
// NG: MFC CATCH マクロ内で Delete() を呼んでいる
CATCH(CFileException, e)
{
e->ReportError();
e->Delete(); // 呼ばない。マクロ側が自動削除する
}
END_CATCH
再スローするときは Delete() しない
例外を上位へ再スローする場合は、その場で Delete() を呼びません。上位の catch で最終的に処理するときに削除します。
try
{
LoadData();
}
catch (CException* e)
{
LogException(e);
throw; // この場では Delete() しない
}
まとめ
- C++
catch (CException* e)では、処理後にe->Delete()を呼びます - MFC
CATCHマクロでは、例外オブジェクトは自動削除されるためDelete()を呼びません delete e;ではなくe->Delete()を使います- 再スローする場合は、その場で
Delete()しません
