ikautak.log

C/C++, Python, CUDA, Android, Linux kernel, Network, etc.

More Effective C++ 項目11 デストラクタから発生した例外を抑える

新訂版 More Effective C++ (AddisonーWesley professional co)

新訂版 More Effective C++ (AddisonーWesley professional co)

  • 作者: スコット・メイヤーズ,安村通晃,伊賀聡一郎,飯田朱美,永田周一
  • 出版社/メーカー: ピアソンエデュケーション
  • 発売日: 2007/06/29
  • メディア: 単行本(ソフトカバー)
  • 購入: 8人 クリック: 129回
  • この商品を含むブログ (43件) を見る

項目11 デストラクタから発生した例外を抑える

デストラクタが呼ばれる状況は二つある。

  • スコープの外に出たり、明示的にdeleteされたりして、オブジェクトが正常な状態で破棄されるケース
  • 例外の伝搬でスタックの巻き戻しの間に、例外処理機構によって破棄されるケース

デストラクタが実行されたとき、例外が生きている時も死んでいる時もある。
例外が生きているという前提でデストラクタを書かなければならない。

例外が生きているとき、デストラクタから制御が離れると、C++はterminate関数を呼ぶが、この関数は局所オブジェクトを破棄する間もなくプログラムを停止する。

コンピュータのセッションを管理するクラスを考える。

class Session {
public:
    Session();
    ~Session();
    ...
private:
    static void logCreation(Session* obj);
    static void logDestruction(Session* obj);
};

logCreationとlogDestructionはオブジェクトの生成と破棄を記録する。
デストラクタは次のようなもの。

Session::~Session() {
    logDestruction(this);
}

logDestructionが例外を発生させたとき、Sessionは例外を受け取らないので、呼び出し元に伝搬する。
もし、他の例外が発生してデストラクタが呼び出されていた場合はterminate関数が起動され、プログラムが停止する。

Sessionデストラクタから外に例外が伝搬しないようにtry/catchを使うと、以下のようになる。

Session::~Session() {
    try {
        logDestruction(this);
    } catch (...) {
        cerr << "Unable to log destruction of Session.\n";
    }
}

しかし、これは安全ではない。cerrで例外が発生するとterminateが起動され、最初の問題と同様になる。
よって、デストラクタで例外が発生したら何もしないようにする。

Session::~Session() {
    try {
        logDestruction(this);
    } catch (...) {
        // 何もしない
    }
}

こうすることで、デストラクタで発生した例外が外に伝搬することを防ぎつつ、例外が生きていてスタック巻き戻しの一貫でデストラクタが実行された場合でも、tarminateが呼ばれることはない。

また、デストラクタから例外を伝搬させると、デストラクタは完了せず、例外が発生した時点で止まってしまう。

まとめ
以下の理由で、デストラクタでの例外は伝搬させてはならない。

  • スタック巻き戻しの一貫で、terminateを呼ばないようにするため
  • デストラクタで完了すべき終了処理が常に呼ばれることを保証するため