13

C++ で RAII を使い始めたばかりで、小さなテスト ケースをセットアップしています。コードがひどく混乱しているか、RAII が機能していません! (前者だと思います。)

私が実行した場合:

#include <exception>
#include <iostream>
class A {
public:
    A(int i) { i_ = i; std::cout << "A " << i_ << " constructed" << std::endl; }
    ~A() { std::cout << "A " << i_ << " destructed" << std::endl; }
private:
    int i_;
};

int main(void) {
    A a1(1);
    A a2(2);
    throw std::exception();
    return 0;
}

コメントアウトされた例外を除いて、次のようになります。

A 1 constructed
A 2 constructed
A 2 destructed
A 1 destructed

予想通りですが、例外があります:

A 1 constructed
A 2 constructed
terminate called after throwing an instance of 'std::exception'
  what():  std::exception
Aborted

そのため、スコープ外に出てもオブジェクトは破壊されません。これは RAII のすべての基礎ではありませんか。

ポインタと修正は大歓迎です!

4

10 に答える 10

20

例外のハンドラーがありません。これが発生すると、標準では std::terminate が呼び出され、次に中止が呼び出されます。The C++ Programming Language, 3rd edition のセクション 14.7 を参照してください。

于 2009-07-09T14:16:38.230 に答える
17

問題は、それmainが特別なステータスを持っていることです。そこから例外がスローされると、スタックは意味のある巻き戻しができず、std:terminate代わりにアプリケーションが呼び出すだけです。

そして、変数がスコープ外に出ない理由が少しわかります。それらが宣言されたスコープから実際には離れていません。何が起こるかは、これと同等であると考えることができます:

int main(void) {
  A a1(1);
  A a2(2);
  std::terminate();
}

(ただし、この場合デストラクタが呼び出されるかどうかは実装定義であると考えているため、一部のプラットフォームでは期待どおりに機能します)

于 2009-07-09T14:18:24.700 に答える
6

メインに未処理の例外があります。これは、終了の呼び出しを意味します。これを試して:

int main(void)
{
    try
    {
        A a1(1);
        A a2(2);
        throw std::exception();
        return 0;
    }
    catch(const std::exception & e)
    {
        return 1;
    }


}
于 2009-07-09T14:13:08.513 に答える
5

他の人が指摘したように、terminate() を呼び出すキャッチされない例外があります。この場合にデストラクタが呼び出されるかどうかは実装定義 (標準、15.3 パラグラフ 9 および 15.5.1 パラグラフ 2 を参照) であり、実装での定義は明らかに「いいえ、呼び出されません」です。(terminate() がハンドラーを持たない例外をスローする以外の理由で呼び出された場合、デストラクタは呼び出されません。)

于 2009-07-09T14:23:11.743 に答える
4

std::terminate が呼び出されているため、 A オブジェクトは破棄されていません。

std::terminate は、未処理の例外がメインから漏れたときに呼び出されます。コードを try/catch でラップすると (catch が単に再発生する場合でも)、予期した動作が表示されます。

于 2009-07-09T14:26:18.993 に答える
3

例外を適切に処理していないため、オブジェクトが範囲外になる前にアプリケーションが終了しています。

もう少し説明します。例外がメインに「バブル」する場合、スタックは巻き戻されます (編集)。コードを二次関数に移動しても、この問題は解決されません。元:

      1 #include <exception>
      2 #include <iostream>
      3
      4 void test();
      5
      6 class A {
      7     public:
      8         A(int i) { i_ = i; std::cout << "A " << i_ << " constructed" << std::endl; }
      9         ~A() { std::cout << "A " << i_ << " destructed" << std::endl; }
     10     private:    int i_;
     11 };
     12
     13
     14 int main(void) {
     15     test();
     16     return 0;
     17 }
     18
     19 void test(){
     20             A a1(1);
     21             A a2(2);
     22            throw std::exception();
     23 }

上記のコードは問題を解決しません。これを解決する唯一の方法は、スローされた例外を try-catch ブロックでラップすることです。これにより、例外がメインに到達するのを防ぎ、オブジェクトが範囲外になる前に発生している終了を停止します。

于 2009-07-09T14:15:50.757 に答える
1

他の人は、これを処理するために内部に try/catch を入れることを提案していますがmain()、これは正常に機能します。なんらかの理由で、めったに使用されない「function-try-block」の見栄えが良くなり、驚きました (奇妙すぎると思いました)。しかし、私は本当の利点はないと思います:

int main(void) 
try
{
    A a1(1);
    A a2(2);
    throw std::exception();
    return 0;
}
catch (...) 
{
    throw;
}

いくつかの欠点は、めったに使用されないため、多くの開発者がループを見つけたときにスローされ、それが考慮されている場合は VC6 がチョークすることです。

于 2009-07-10T02:39:17.167 に答える
0

例外はmain()に到達するまでに処理されないため、std :: terminate()が呼び出されます。これは、基本的に次のようになります。

int main(void) {
  A a1(1);
  A a2(2);
  exit(1);
}

スコープ外になる前にプログラムが終了した場合に、デストラクタが呼び出される保証はありません。RAIIの別の穴については、次のことを考慮してください。

int main(void) {
  A *a1 = new A(1);
}
于 2009-07-09T17:04:28.033 に答える
0

次のコードは機能します。

#include <exception> 
#include <iostream> 

class A { 
public: 
    A(int i) { i_ = i; std::cout << "A " << i_ << " constructed" << std::endl; } 
    ~A() { std::cout << "A " << i_ << " destructed" << std::endl; } 
private: 
    int i_; 
}; 

void test() {
    A a1(1); 
    A a2(2); 
    throw std::exception(); 
} 

int main(void) { 
 try {
  test();
 } catch(...) {
 }
    return 0; 
} 
于 2010-02-05T09:22:45.460 に答える