【MFC】try-catch (C++) vs CATCH (MFCマクロ) の違い

【MFC】try-catch (C++) vs CATCH (MFCマクロ) の違い

古い MFC プロジェクトを引き継ぐと、例外処理に TRY / CATCH / END_CATCH という MFC 独自マクロが使われていることがあります。この記事では、C++ 標準の try-catch と MFC マクロの違いを、構文CException の寿命管理移行判断の観点で整理します。


目次

まず結論:新規コードは try-catch、既存マクロは無理に壊さない

状況選ぶ書き方理由
新規に例外処理を書くC++ 標準の try-catch現在の C++ と相性がよく、std::exception なども扱いやすい
既存コードに MFC マクロが大量にあるすぐ全置換しない動いている例外経路を壊すリスクがあるため、変更範囲を絞る
MFC 例外を C++ catch で受けるcatch (CException* e)MFC の throw 関数は例外オブジェクトをポインタで投げる
CException::Delete() の扱いマクロ版と C++ 版で異なるMFC マクロは自動削除、C++ catch は自分で削除する

一番間違えやすいのは CException::Delete() の扱いです。MFC の CATCH マクロで捕捉した例外オブジェクトは、マクロ側で自動的に削除されます。自分で e->Delete() を呼んではいけません。一方、C++ の catch キーワードで CException* を捕捉した場合は、自動削除されないため e->Delete() が必要です。

CException::Delete() の所有権ルールは、【MFC】CException::Delete: なぜ Delete() 呼び出しが必須かで詳しく整理しています。


MFC マクロの構文

MFC の例外マクロは、C++ 例外処理構文が一般化する前から使われていた互換用の書き方です。構文は次のようになります。

TRY
{
    CFile file;
    file.Open(_T("data.dat"), CFile::modeRead);
    // ... ファイル操作 ...
}
CATCH(CFileException, e)
{
    // e は CFileException* として使える
    TCHAR szError[256]{};
    e->GetErrorMessage(szError, 256);
    AfxMessageBox(szError);

    // ここで e->Delete() は呼ばない。
    // CATCH マクロが例外オブジェクトを自動削除する。
}
END_CATCH
  • CATCH の引数は、例外クラス名とポインタ変数名です
  • 例外オブジェクトは CFileException* のようにポインタとして扱います
  • CATCH マクロで捕捉した例外は、マクロの終了時に自動削除されます
  • AND_CATCH で複数の MFC 例外型を捕捉できます
  • END_CATCH で例外処理ブロックを閉じます

C++ 標準の try-catch で書く場合

同じ処理を C++ 標準の try-catch で書くと、次のようになります。MFC の例外を CException* として捕捉した場合は、最後に Delete() を呼びます。

try
{
    CFile file;
    if (!file.Open(_T("data.dat"), CFile::modeRead))
    {
        AfxThrowFileException(CFileException::fileNotFound,
                              -1,
                              _T("data.dat"));
    }
    // ... ファイル操作 ...
}
catch (CFileException* e)
{
    TCHAR szError[256]{};
    e->GetErrorMessage(szError, 256);
    AfxMessageBox(szError);

    // C++ catch では自動削除されないため必要
    e->Delete();
}
catch (const std::exception& ex)
{
    CString msg(ex.what());
    AfxMessageBox(msg);
}

C++ 標準の try-catch では、MFC 例外だけでなく std::exception 系の例外も同じ構文で扱えます。新規コードではこちらに寄せる方が読みやすく、MFC 以外の C++ コードとも混在しやすくなります。


比較表

観点MFC マクロC++ 標準 try-catch
構文TRY / CATCH / AND_CATCH / END_CATCHtry / catch
MFC 例外の受け取り方CATCH(CFileException, e)catch (CFileException* e)
CException::Delete()呼ばない。マクロ側が自動削除するCException* を捕捉したら呼ぶ
std::exception の捕捉対象外catch (const std::exception&) で捕捉できる
複数型の捕捉AND_CATCH で連鎖複数の catch ブロック
新規コードでの読みやすさレガシー色が強い現代 C++ として読みやすい
主な使いどころ既存 MFC コードの保守新規コード、移行後のコード

CException::Delete() の判断を間違えない

CException::Delete() は、例外オブジェクトがヒープ上に作られている場合だけ安全に削除するための MFC の関数です。delete e; を直接呼ぶのではなく、必要な場面では e->Delete() を使います。

捕捉方法Delete() は必要か理由
CATCH(CFileException, e)不要。呼ばないMFC マクロが自動的に呼ぶ
catch (CFileException* e)必要C++ catch キーワードでは自動削除されない
catch (const std::exception& ex)不要MFC の CException* ではない

この違いを混同すると、メモリリークや二重解放につながります。古いコードを移行するときは、単純に CATCHcatch へ置き換えるだけでなく、Delete() の責任がどちらにあるかを必ず確認します。


マクロから標準 try-catch への書き換え手順

書き換えは機械的に見えますが、Delete() の責任が変わる点だけは必ず確認します。

MFC マクロC++ 標準注意点
TRY {try {ブロック構造を確認する
CATCH(CXxxException, e) {catch (CXxxException* e) {標準 catch では最後に e->Delete() を追加する
AND_CATCH(CYyyException, e) {catch (CYyyException* e) {複数の catch に分ける
END_CATCH削除} の対応を崩さない
THROW(pException)throw pException;所有権が移る前提を確認する
THROW_LAST()throw;現在の例外処理中だけ使える

既存コードが安定しているなら、プロジェクト全体を一気に置き換える必要はありません。新規に触るファイル、または例外処理を修正するファイル単位で標準 try-catch に寄せるのが現実的です。


まとめ

  • 新規コードは C++ 標準の try-catch を使う方が読みやすいです
  • 既存の MFC マクロは、安定しているなら無理に一括置換する必要はありません
  • MFC マクロの CATCH では、例外オブジェクトは自動削除されます
  • C++ catch (CException* e) では、自分で e->Delete() を呼びます
  • 移行時は、構文だけでなく例外オブジェクトの所有権も確認します
目次