C++
 Computer >> コンピューター >  >> プログラミング >> C++

【C++解説】例外処理時にデストラクタはどう動く?オブジェクト破棄の仕組みを実例で理解する

C++において、デストラクタはオブジェクトが破棄されるタイミングで自動的に呼び出され、確保していたメモリなどのリソースをシステムへ解放します。そして重要なのが例外処理との関係です。クラス内で例外がスローされた場合、catchブロックが実行される前に、すでに構築済みのオブジェクトに対してはデストラクタが自動的に呼び出されます。この仕組みは「スタック巻き戻し(stack unwinding)」と呼ばれ、C++の例外安全な設計において非常に重要な概念です。

ただし、一つ注意点があります。コンストラクタの中で例外が発生した場合、そのオブジェクトは「構築が完了していない」状態であるため、そのオブジェクト自身のデストラクタは呼び出されません。一方で、それより前に正常に構築された他のオブジェクトのデストラクタは確実に実行されます。

本記事では、サンプルコードとその実行結果を通じて、この挙動を具体的に確認していきます。

アルゴリズム

開始
    クラスSample1を宣言する。
        Sample1のコンストラクタを宣言する。
            「Construct an Object of sample1」と出力する。
        Sample1のデストラクタを宣言する。
            「Destruct an Object of sample1」と出力する。
    クラスSample2を宣言する。
        Sample2のコンストラクタを宣言する。
            整数型の変数iを宣言する。
            i を 7 で初期化する。
            「Construct an Object of sample2」と出力する。
            i をスロー(throw)する。
        Sample2のデストラクタを宣言する。
            「Destruct an Object of sample2」と出力する。
    tryブロック:
        Sample1型のオブジェクトs1を宣言する。
        Sample2型のオブジェクトs2を宣言する。
    catch(int i):
        「Caught」を出力する。
        変数iの値を出力する。
終了

サンプルコード

#include <iostream>
using namespace std;

class Sample1 {
public:
    Sample1() {
        cout << "Construct an Object of sample1" << endl;
    }
    ~Sample1() {
        cout << "Destruct an Object of sample1" << endl;
    }
};

class Sample2 {
public:
    Sample2() {
        int i = 7;
        cout << "Construct an Object of sample2" << endl;
        throw i;
    }
    ~Sample2() {
        cout << "Destruct an Object of sample2" << endl;
    }
};

int main() {
    try {
        Sample1 s1;
        Sample2 s2;
    } catch(int i) {
        cout << "Caught " << i << endl;
    }
}

実行結果

Construct an Object of sample1
Construct an Object of sample2
Destruct an Object of sample1
Caught 7

実行結果の解説

この出力には、押さえておくべきポイントがいくつかあります。

  1. 例外の発生:まずs1が正常に構築され、続いてs2のコンストラクタ内で整数値7を持つ例外がスローされます。
  2. s1のデストラクタだけが実行される:スタック巻き戻しの過程で、すでに構築が完了していたs1のデストラクタが呼び出され、「Destruct an Object of sample1」が出力されます。
  3. s2のデストラクタは実行されない:s2はコンストラクタが完了する前に例外が発生したため、オブジェクトとして成立していません。そのため「Destruct an Object of sample2」は出力されません。
  4. catchブロックの実行:最後にcatchブロックが例外を受け取り、「Caught 7」を出力してプログラムが終了します。

このように、C++の例外処理では「完全に構築されたオブジェクトだけが破棄の対象となる」という規則に従ってデストラクタが実行されます。ファイルハンドルやメモリなどを扱うRAII(Resource Acquisition Is Initialization)形式のクラスを設計する際には、この挙動を正しく理解しておくことが、リークのない堅牢なコードを書くうえで非常に重要です。

  1. C++の例外処理入門!try・catch・throwの基本と標準例外クラス一覧

    C++における例外処理(Exception Handling)とは、プログラムの実行時に発生するエラー(ランタイムエラー)を適切に扱うための仕組みです。例外とは、実行時に送出(スロー)されるイベントのことで、C++ではすべての標準例外が std::exception クラスから派生しています。もし例外を捕捉しなかった場合、例外メッセージが出力され、プログラムは強制終了してしまいます。 C++標準では、例外関連のクラスが <exception> ヘッダに定義されており、プログラムの中で自由に利用できます。親子関係によるクラス階層は次のようになっています。 C++の主な標準例外クラス

  2. C#の例外処理とは?try・catch・finally・throwの基本をサンプルコードで解説

    例外(Exception)とは、プログラムの実行中に発生する問題やエラーのことを指します。C#では、try、catch、finally、throwという4つのキーワードを使って、こうした例外を適切に処理することができます。 例外処理で使う4つのキーワード try tryブロックは、例外が発生する可能性のあるコードを囲み、その範囲に対して例外処理を有効にします。監視対象となる処理は、このブロック内に記述します。 catch catchキーワードは、実際に発生した例外を捕捉(キャッチ)する役割を担います。tryブロック内で例外がスローされると、対応するcatchブロックが実行されます。 f