6

次のようなクラスがあるとします。

#include <iostream>

using namespace std;

class Boda {
    private:
        char *ptr;

    public:
        Boda() {
            ptr = new char [20];
        }
        ~Boda() {
            cout << "calling ~Boda\n";

            delete [] ptr;
        }

        void ouch() {
            throw 99;
        }
};

void bad() {
    Boda b;
    b.ouch();
}

int main() {
    bad();
}

デストラクタ~Bodaが呼び出されることはないようです。したがって、ptrリソースが解放されることはありません。

プログラムの出力は次のとおりです。

terminate called after throwing an instance of 'int'
Aborted

だから私の質問への答えはNoです。

しかし、例外がスローされたときにスタックが巻き戻されたと思いましたか?Boda b私の例では、なぜオブジェクトが破壊されなかったのですか?

このリソースの問題を理解するのを手伝ってください。将来はもっといいプログラムを書きたいです。

また、これはいわゆるRAIIですか?

ありがとう、BodaCydo。

4

3 に答える 3

9

例外がどこにもキャッチされない場合、C ++ランタイムは、スタックの巻き戻しやデストラクタの呼び出しを行わずに、プログラムを直接終了することができます。

ただし、への呼び出しの周りにtry-catchブロックを追加すると、呼び出されているオブジェクトbad()のデストラクタが表示されます。Boda

int main() {
    try {
      bad();
    } catch(...) {  // Catch any exception, forcing stack unwinding always
      return -1;
    }
}

RAIIは、動的に(ヒープ)割り当てられたメモリが、オブジェクトが破棄されたときに割り当てを解除する、自動的に(スタック)割り当てられたオブジェクトによって常に所有されることを意味します。これは、通常の戻りまたは例外のいずれかが原因で、自動的に割り当てられたオブジェクトがスコープ外になったときにデストラクタが呼び出されるという保証に依存しています。

通常、デストラクタを実行する主な理由はメモリを解放することであり、プログラムが終了するとすべてのメモリがOSに戻されるため、このコーナーケースの動作は通常RAIIに関しては問題になりません。ただし、デストラクタがディスク上のロックファイルを削除するなど、より複雑なことを行う場合は、クラッシュ時にプログラムがデストラクタを呼び出すかどうかに違いが生じる場合は、mainキャッチするtry-catchブロックでラップすることをお勧めします。スタックが終了する前に常に巻き戻されるようにするためだけに、すべて(とにかく例外で終了するためだけに)。

于 2010-06-27T23:53:05.620 に答える
2

コンストラクタで例外が発生した場合、デストラクタは実行されません。

例のように別のメソッドで例外が発生した場合、必要に応じて(例外がどこかで処理された場合)実行されます。ただし、プログラムが終了するため、ここではデストラクタを呼び出す必要はなく、動作はコンパイラによって異なります...

の考え方RAIIは、コンストラクタがリソースを割り当て、デストラクタがそれらを解放するというものです。コンストラクターで例外が発生した場合、割り当てられたリソースと割り当てられなかったリソースを簡単に知る方法はありません(例外が発生したコンストラクター内の正確な場所によって異なります)。また、コンストラクターが失敗した場合、コンストラクターを呼び出して例外を発生させ、割り当てられたメモリが解放される(スタックの巻き戻しまたはヒープに割り当てられたメモリのいずれか)という唯一の方法は、コンストラクターが割り当てられなかったかのようになります。

解決策は明らかです。コンストラクター内で例外が発生する可能性がある場合は、それをキャッチし、必要に応じて割り当てられたリソースを解放する必要があります。実際には、デストラクタで重複したコードである可能性がありますが、それは大きな問題ではありません。

デストラクタでは、スタックの巻き戻しで大きな問題が発生する可能性があるため、例外を発生させないでください。

他の方法では、必要に応じて例外を使用しますが、どこかで例外を処理することを忘れないでください。未処理の例外は、例外がまったくない場合よりも悪い場合があります。いくつかのマイナーエラーの例外を処理しないプログラムを知っています...そして警告を出すだけのエラーでクラッシュします。

于 2010-06-28T00:29:38.603 に答える
1

ストリームをフラッシュしてみてください。デストラクタが実際に呼び出されていることがわかります。

cout << "calling ~Boda" << endl;

プログラムの終了が実際の出力の前に割り込むポイントまで印刷を遅らせるのは、I/Oのバッファリングです。

編集:

上記は、処理された例外にも当てはまります。未処理の例外を除いて、標準ではスタックが巻き戻されているかどうかは指定されていません。このSOの質問も参照してください。

于 2010-06-27T23:55:06.160 に答える