例外処理 : 例外処理でのオブジェクトの解放
更新 : 2007 年 11 月
ここでは、例外が発生したときにオブジェクトを解放する理由と方法について説明します。ここでは、次の内容について説明します。
例外のローカル処理
オブジェクト削除後の例外のスロー
フレームワークまたはアプリケーションが例外をスローすると、プログラムの正規の流れが妨げられます。したがって、ヒープ領域に作成したオブジェクトを追跡管理し、例外が発生したときに確実に削除する必要があります。
次の 2 つの方法があります。
try キーワードおよび catch キーワードを使って例外をローカルに処理してから、すべてのオブジェクトを 1 行のステートメントで削除します。
catch ブロック内のすべてのオブジェクトを破棄してから、catch ブロックの外で例外をスローして処理します。
次のコード例の問題は、上の 2 つの方法で解決できます。
void SomeFunc() // Problematic code
{
CPerson* myPerson = new CPerson;
// Do something that might throw an exception.
myPerson->SomeFunc();
// Now destroy the object before exiting.
// If SomeFunc above throws an exception this code will
// not be reached and myPerson will not be deleted.
delete myPerson;
}
上の例では、SomeFunc が例外をスローしても myPerson は削除されません。処理の流れがその外側の例外ハンドラに直接ジャンプするので、関数の正常終了とオブジェクトを削除するコードがバイパスされます。関数から例外がスローされると、オブジェクトを指すポインタがスコープから出るので、このオブジェクトが記憶されているメモリ領域は、プログラムが実行されている限り解放されません。この状態はメモリ リークと呼ばれ、メモリ診断機能を使って検出されます。
例外のローカル処理
try/catch を使ったプログラムでは、例外が発生するとオブジェクトが削除されるので、メモリ リークを防止できます。上のプログラムは、次のように書き換えることができます。
void SomeFunc()
{
CPerson* myPerson = new CPerson;
try
{
// Do something that might throw an exception.
myPerson->SomeFunc();
}
catch( CException* e )
{
// Handle the exception locally
e->Delete();
}
// Now destroy the object before exiting.
delete myPerson;
}
修正したプログラムでは、例外ハンドラでキャッチした例外をローカルに処理しています。その後、関数を正常に終了してオブジェクトを削除します。このコードの特徴は、例外をキャッチするコンテキストが try/catch ブロックで記述されている点です。ローカルな例外フレームがないと、関数は例外がスローされたことを検出できないので、正常終了して、オブジェクトを削除することはできません。
オブジェクト削除後の例外のスロー
例外を処理するもう 1 つの方法は、1 つ外側にある別の例外処理コンテキストに例外を引き渡すことです。この場合、catch ブロックでは、ローカルに割り当てられたオブジェクトを削除してから、例外をスローします。
スロー側の関数は、オブジェクトが占有していたヒープ領域を解放する場合としない場合があります。平常時にヒープ オブジェクトを解放してから復帰する関数の場合は、必ずヒープ領域を解放してから、例外をスローしてください。一方、平常時にオブジェクトを解放せずに復帰する関数の場合は、ヒープ オブジェクトの解放が必要かどうかをそのつど判定する必要があります。
次に、ローカルに割り当てたオブジェクトを削除する例を示します。
void SomeFunc()
{
CPerson* myPerson = new CPerson;
try
{
// Do something that might throw an exception.
myPerson->SomeFunc();
}
catch( CException* e )
{
e->ReportError();
// Destroy the object before passing exception on.
delete myPerson;
// Throw the exception to the next handler.
throw;
}
// On normal exits, destroy the object.
delete myPerson;
}
例外処理機構は、自動的にフレーム オブジェクトを削除します。フレーム オブジェクトのデストラクタも呼び出されます。
例外をスローしうる関数を呼び出すときは、try/catch ブロックを使うと、例外を確実にキャッチし、作成したオブジェクトを削除できます。MFC 関数の多くは例外をスローします。
詳細については、「例外処理 : 例外のキャッチと削除」を参照してください。