6
class XX
{
public:
    static unsigned s_cnt;
    XX()
    {
        ++ s_cnt;
        std::cout << "C XX " << s_cnt << "\n";

        if ( s_cnt > 2 )
            throw std::exception();
    }

    //private:
    ~XX()
    {
        std::cout << "~ XX\n";
    }
};
unsigned XX::s_cnt = 0;

int main()
{
    try
    {
        XX *xx = new XX[10];
    } catch ( ... )
    {
        std::cout << "Exc\n";
    }
}

出力:

C XX 1
C XX 2
C XX 3
~ XX
~ XX
Exc

しかし、try-catch を削除すると、次のように表示されます。

C XX 1
C XX 2
C XX 3
terminate called after throwing an instance of 'std::exception'
  what():  std::exception
zsh: abort      ./a.out

C++ が最初のケースではデストラクタを呼び出すのに、2 番目のケースでは呼び出さないのはなぜですか?

4

2 に答える 2

15

例外をキャッチしない場合 (つまり、キャッチされない例外になり、プログラムを終了する場合)、C++ はデストラクタが実際に呼び出されることを保証しません。

これにより、コンパイラは例外処理を実装する方法に余裕ができます。たとえば、GCC は最初にハンドラーを検索します。見つからない場合は、すぐに中止し、デバッグ用に完全なスタック情報を保持します。見つかった場合は、ハンドラーに到達するまで実際にスタックを巻き戻し、オブジェクトを破棄します。出力が表示されないのはそのためです。プログラムは、オブジェクトを破棄する前に中止されます。

于 2013-08-28T15:19:58.307 に答える
3

コンストラクターから例外をスローすると、標準はオブジェクトを構築されていないものとして扱います。存在しないものを破壊することはありませんよね?

実際、この単純な例でさえ、あなたが示唆するようには機能しません:

struct A {
    A() {throw std::exception();}
    ~A() {std::cout << "A::~A()" << std::endl;}
};

int main() {
    A a;
}

これはキャッチされない例外で終了しますが、「A::~A()」は出力されません。

考えてみれば、それがセマンティックを与える唯一の可能な方法です。

たとえば、型 A をクラス B のメンバー オブジェクトとして使用できます。

struct B {
    B() : m_a(),m_p(new int) {}
    ~B() {delete m_p;}

    A m_a;
    int * m_p;
};

明らかにB::B()すぐにスローします(初期化さえしませんm_p)。この場合、仮の C++ 標準で呼び出しB::~B()が義務付けられている場合、デストラクタにm_pは が初期化されているかどうかを知る方法がありません。

つまり、コンストラクターから例外をスローするということは、オブジェクトが存在せず、その有効期間が開始されなかったことを意味します。このGotWはそれについてかなり明確です.

おまけ: の定義でBと の順序を入れ替えるm_aとどうなるm_pでしょうか? 次に、メモリリークがあります。そのため、メンバー オブジェクトの初期化中に例外をキャッチする特定の構文が存在します。

于 2013-08-28T15:34:54.027 に答える