- ベストアンサー
コンストラクタでnewを失敗した場合の対応について
よろしくお願いします。 クラスのメンバで3つのlongのポインタを宣言し、 コンストラクタ内でそれぞれにnewして領域を確保 しています。 質問1)newを失敗した場合には、そのポインタに 対してdeleteしてはいけないのでしょうか? 質問2)上記3つもエリアの確保のうち、2つめで失 敗した場合、1つ目のdeleteをしてやる 必要はあるのでしょうか? catch(bad_alloc)でその処理をしようとおもうのですが、そもそもコンストラクタで例外を発生させるなと かかれている書籍もあるようです。ただ、すでにそういう記載になってしまっており、できれば、いまの構造でメモリーリークを防げないかと思案しております。どなたか、よい方法をご存知の方いらっしゃいましたら、アドバイスいただけましたら幸いでございます。
- みんなの回答 (5)
- 専門家の回答
質問者が選んだベストアンサー
#2です。 > たとえば、bのnewでアロケーションを失敗した場合、 > Cのポインタは0で初期化された状態で、deleteしても > 問題ないのでしょうか? > また、Bのnewで失敗した状態のBにもdeleteを実行することになりますが、問題ないのでしょうか? > deleteの順番がポイントなのでしょうか? #3でも書かれていますが、0, NULLのdeleteは問題ありません。 deleteの順番はこの場合はどうでもいいです。 > また、この場合デストラクタA::~A()は実行されるのでしょうか? されません。 例えば A *objA = 0; try { objA = new A; } catch (...) { } で例外が発生した場合、newの時点で例外発生->catchブロックへジャンプするので objAに代入されず、objAは0のままです。 このため、objAのデストラクタを呼ぶことはできません。 したがってコンストラクタで例外が発生する場合は、 例外発生前にコンストラクタ内で確保したりソースを解放してからthrowしなければ そのリソースを解放する手段がなくなります。
その他の回答 (4)
- jacta
- ベストアンサー率26% (845/3158)
少し出遅れましたが、ひとつずつ回答していきます。 > 質問1)newを失敗した場合には、そのポインタに > 対してdeleteしてはいけないのでしょうか? 原則としては駄目です。 というのも、newに失敗するとstd::bad_alloc例外が創出されるので、そのポインタには何も代入されず、不定状態になります。あらかじめ空ポインタで初期化しておくか、new(std::nothrow)を使って、失敗時に空ポインタが返るようにしておけば問題ありません。 > 質問2)上記3つもエリアの確保のうち、2つめで失敗した場合、1つ目のdeleteをしてやる > 必要はあるのでしょうか 必要です。 根本的な問題を指摘するなら、コンストラクタの中で複数のnewを行うべきではありません。どうしてもの場合は、std::auto_ptrなど、スマートポインタを使うか、内部的にcatchして辻褄を合わせる(#2の方の方法)しかありません。 long型の配列を三つ用意するのであれば、std::vector<long>を三つ使う方が得策です。 # std::auto_ptrを使えないのは、new[]を使うからですよね?
- sha-girl
- ベストアンサー率52% (430/816)
#1です。すいません間違ってました。 ISOによるとNULLに対するdeleteは無視されるようです。 よってnewが失敗したポインタをdeleteしても問題ないようです。
- kmb01
- ベストアンサー率45% (63/138)
自分ならこう書きます。 class A { private: A(); ~A(); long *a,*b,*c; }; A::A() : a(0), b(0), c(0) { try { a = new long; b = new long; c = new long; } catch (...) { delete c; delete b; delete a; throw; //同じ例外をthrow } } A::~A() { delete c; delete b; delete a; } コンストラクタで例外を投げないようにするには、 クラスAは初期化が完全に成功したかを示すフラグメンバ変数を持ち コンストラクタで例外が起こらなかった場合そのメンバをtrueに設定する。 以降メンバ変数にアクセスする全ての関数の冒頭で 初期化が完全に終わっているかチェックする。 となり、変更箇所が多いためお勧めしません。
補足
この方法ですと現在のソースの修正がしやすいので、 もう少し教えていただきたいのですが、 たとえば、bのnewでアロケーションを失敗した場合、 Cのポインタは0で初期化された状態で、deleteしても 問題ないのでしょうか? また、Bのnewで失敗した状態のBにもdeleteを実行することになりますが、問題ないのでしょうか? deleteの順番がポイントなのでしょうか? また、この場合デストラクタA::~A()は実行されるのでしょうか? コンストラクタで例外を投げる方向で設計をしようと思います。よろしくお願いいたします。
- sha-girl
- ベストアンサー率52% (430/816)
>質問1)newを失敗した場合には、そのポインタに対してdeleteしてはいけないのでしょうか? 駄目です。newが失敗したポインタにはNULLが入っています。 >質問2)上記3つもエリアの確保のうち、2つめで失敗した場合、1つ目のdeleteをしてやる必要はあるのでしょうか? 1つ目のdeleteをしてやる必要があります。(解放しないと1つ目のメモリが確保されたままになります。) std::auto_ptrを使ってはどうでしょうか? http://www.doumo.jp/postgretips/tips.jsp?tips=78
お礼
ありがとうございました。 std::auto_ptrが使えればよかったのですが、 移植性を考えて作る必要があり今回は使用しない 方向で設計をせざるを得なくなりました。 勉強になりました。ありがとうございます。
お礼
ありがとうございました。 シンプルでかつ確実な方法なのでこの方法で実装しようと思います。お手数をおかけしました。