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