例外処理 : 例外処理でのオブジェクトの解放
この記事では、例外が発生したときにオブジェクトを解放する必要性と方法について説明します。 ここでは、次の内容について説明します。
フレームワークまたはアプリケーションによってスローされる例外は、通常のプログラム フローに割り込みます。 したがって、例外がスローされた場合に適切に破棄できるように、オブジェクトを詳細に追跡することが非常に重要です。
これを行うための主要な方法が 2 つあります。
try
キーワードとcatch
キーワードを使用して例外をローカルで処理し、1 つのステートメントですべてのオブジェクトを破棄します。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 つの方法は、例外を次の外側の例外処理コンテキストに渡すことです。 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 関数で例外がスローされる可能性があることに注意してください。
詳細については、「例外処理: 例外のキャッチと削除」を参照してください。