コンストラクタの初期化リストの途中で例外が発生した場合、どんな振る舞いになるのか気になったのでためしてみました。
#include <iostream> #include <stdexcept> class Foo { public: explicit Foo(int id, bool bad = false) : id(id) { std::cout << "Foo::Foo(" << id << ")" << std::endl; if(bad) { throw std::runtime_error("BAD!"); } } ~Foo() { std::cout << "Foo::~Foo(" << id << ")" << std::endl; } private: int id; }; class Bar { public: Bar() : foo1(1), foo2(2, true), foo3(3) { std::cout << "Bar::Bar" << std::endl; } ~Bar() { std::cout << "Bar::~Bar" << std::endl; } private: Foo foo1; Foo foo2; Foo foo3; }; int main(int, char* []) { try { Bar bar; } catch(const std::exception& e) { std::cout << "EXCEPTION THROWN : " << e.what() << std::endl; } return 0; }
実行結果。
Foo::Foo(1) Foo::Foo(2) Foo::~Foo(1) EXCEPTION THROWN : BAD!
初期化リストで例外が発生した場合:
- 初期化完了していないオブジェクト(ここではfoo2およびbar)は、デストラクタは呼ばれない
- 初期化されていないオブジェクトはコンストラクタもデストラクタも呼ばれない(当然ですが)
- 初期化が完了しているオブジェクトのデストラクタは呼ばれる(例外がキャッチされる前に)
次に、Barのコンストラクタを書き換えてみた。
Bar() try : foo1(1), foo2(2, true), foo3(3) { std::cout << "Bar::Bar" << std::endl; } catch(...) { std::cout << "exception thrown from Bar's constructor" << std::endl; }
(この書き方については「関数監視ブロック(function-try-block) - エンジニアのソフトウェア的愛情」を参照してください)
実行結果。
Foo::Foo(1) Foo::Foo(2) Foo::~Foo(1) exception thrown from Bar's constructor EXCEPTION THROWN : BAD!
- 初期化が完了しているオブジェクトのデストラクタは呼ばれる(関数監視ブロックであっても例外がキャッチされる前に)