C++でコンストラクタから例外を送出したい時
きっと今年最後の思いつき。
一般にコンストラクタから例外を投げるとリソース漏れの危険性が出てくる。
例外を投げた当のオブジェクトが自身のリソースをきちんと処理していなかったり、コンストラクタから例外を投げるクラスをnewした時にインスタンスが宙ぶらりんになっちゃったり。
なのでリソース漏れが嫌ならコンストラクタから絶対に例外を投げないようにすべき。
だがそうすると今度はpublicなエラーチェックメソッドを設ける必要が出たり、空のオブジェクトが存在する事になってしまったりする。
そうすると色々手間が増えてしまったりスマートさが無くなって残念な気持ちになってしまう。表
--- 追記ここから ---
コンストラクタから例外を投げる時、そのクラスがnewされたならインスタンス用に割り当てようとした領域は自動で破棄されるらしい。デストラクタ呼ばれないけど。
破棄されるっていう記述も破棄されないっていう記述も日本語資料じゃ同程度にしかネットで見つからない。英語資料の山から探し出すなんて嫌になっちゃうのでやりません。日本語でさえググった結果何十ページも見るのはしんどかったのに。
問題なのはそれが仕様なのかどうかという事。規格書読めって事なんだろうけど規格書ドコー。
とりあえずはコンストラクタ内で例外を処理しきってしまう方向で頑張る。
--- 追記ここまで ---
--- 追記(09/12/31)ここから ---
一番気になっていた事柄の回答がEffectiveC++第三版の52項に記載されていました。
コンストラクタで例外を投げるクラスAが存在する時、new A;でAのコンストラクタが例外を投げると、A用に確保されたメモリ領域は実行したnewに対応するdeleteが自動で呼び出されてA用のメモリ領域がdeleteされます。
ただし、Aはコンストラクトが完了する前に例外を送出していますので、deleteされてもデストラクタは呼ばれません。基底クラスのデストラクタは呼ばれるかもしれません。
またそのdelete作業中にAのコンストラクタ内でインスタンス化に成功しているメンバは次々と破棄されていき、デストラクタが呼ばれていきます。
--- 追記(09/12/31)ここまで ---
なのでデザパタのファクトリパターン使えばいいんじゃなかろうか。
class SampleClass { private: SampleDataClass *m_data; bool m_valid; public: class Exception {}; static SampleClass* create(const SampleDataClass &data) { try { SampleClass *obj = new SampleClass(data); } catch(std::bac_alloc) { throw Exception; } if(!obj->m_valid) { throw Exception; } return obj; } SampleClass(const SampleDataClass &data) : m_data(NULL) , m_valid(true) { try { m_data = new SampleDataClass(data); } catch(std::bad_alloc) { m_valid = false; } } ~SampleClass() { delete m_data; } };
newするメンバが複数必要ならスマートポインタを使えば良し。
んでこれを使う側は下の通り。
try { std::auto_ptr<SampleClass> pointer(SampleClass::create(data)); } catch(SampleClass::Exception) { // 何かする必要があればする。 }
これでリソース漏れとも完全にオサラバじゃね?
欠点はたくさんインスタンス作りたい時に処理が遅くなること。
何か得意気に書いてから思ったけど、これくらい普通に行われてますよねー。
しかも自分で書いておいて何ですが、このやり方はどこかで見たような気がしてしょうがない。
本題とは全く関係が無いけど、Qtのクラスって例外投げるのだろうか・・・。