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ドキュメントでも、MFCマクロで捕捉した場合はCExceptionオブジェクトが自動削除され、C++ のcatchキーワードで捕捉した場合は自動削除されない、と説明されています。
なぜdeleteではなくDelete() なのか
CException::Delete() は、例外オブジェクトがヒープ上に作られている場合だけ削除するための関数です。MFCの例外オブジェクトは、ヒープ上に作られることも、スタック上またはグローバルなオブジェクトとして扱われることもあります。
// CException::Delete の概念
void CException::Delete()
{
if (m_bAutoDelete)
{
delete this;
}
}
delete e; を直接呼ぶと、ヒープ上ではない例外オブジェクトに対してdeleteしてしまう可能性があります。そのため、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() しない
}
次に読む記事
- 【MFC】AfxThrowMemoryException: メモリ不足を優雅に伝える
- 【MFC】try-catch (C++) vs CATCH (MFCマクロ) の違い
- 【MFC】Memory Leak Detection: _CrtSetDbgFlagでリークを自動報告させる
まとめ
- C++ catch (CException* e) では、処理後にe->Delete() を呼びます
- MFC CATCHマクロでは、例外オブジェクトは自動削除されるためDelete() を呼びません
- delete e; ではなくe->Delete() を使います
- 再スローする場合は、その場でDelete() しません
