1

この質問は、デストラクタが呼び出された後にオブジェクトを使用することに触発されています

次のコードを考えてみましょう

class B 
  {
  public:
    B() { cout << "Constructor B() " << endl; }
    ~B() { cout << "Destructor ~B() " << endl; }
  };

class A {
public:
  B ob;
  A()
      try
      { 
      throw 4;
      }
    catch(...)
      {
      cout << "Catch in A()\n";
      }

  A(int)
    {
    try
      {
      throw 4;
      }
    catch(...)
      {
      cout << "Catch in A(int)\n";
      }
    }
  };

int main()
  {
  try
  {
      A f;
  }
  catch (...)
  {
      cout << "Catch in main()\n\n";
  }
  A g(1);
  }

その出力は

Constructor B() 
Destructor ~B() 
Catch in A()
Catch in main()

Constructor B() 
Catch in A(int)
Destructor ~B() 

とは対照的にA(int)、コンストラクターA()には初期化リストの try/catch 構文があります。サブオブジェクトの破壊の順序に違いがあるのはなぜですか? でスローされた例外が にA()伝播するのはなぜmain()ですか?

4

3 に答える 3

3

サブオブジェクトの破壊の順序に違いがあるのはなぜですか?

A(int) でキャッチすると、すべてのサブオブジェクトが有効になり、それらを使用できます。さらに、キャッチ後、オブジェクトの構築を続行し、ユーザーが適切に構築したオブジェクトに「戻る」ことができます。

A() でキャッチすると、構築されたすべてのサブオブジェクトが破棄されます。また、少なくとも現在の ISO C++ 構文では、どのサブオブジェクトが構築され、どのサブオブジェクトが構築されていないかはわかりません。

A() でスローされた例外が main() に伝播するのはなぜですか?

GotW #66 を確認します。

ハンドラー本体にステートメント「throw;」が含まれている場合 その場合、catch ブロックは明らかに、A::A() または B::B() が発行した例外を再スローします。それほど明白ではありませんが、標準で明確に述べられているのは、catch ブロックがスローされず (元の例外を再スローするか、何か新しいものをスローする)、制御がコンストラクターまたはデストラクタの catch ブロックの最後に到達した場合、元の例外は自動的に再スローされます。

これが何を意味するか考えてみてください: コンストラクタまたはデストラクタ function-try-block のハンドラ コードは、何らかの例外を発行して終了する必要があります。他に方法はありません。言語は、発行される例外が何であるかを気にしません。元の例外でも、他の翻訳された例外でもかまいませんが、例外が存在する必要があります。ベースまたはメンバーのサブオブジェクト コンストラクターによってスローされた例外が、それらを含むコンストラクターを超えて何らかの例外がリークするのを防ぐことは不可能です。

簡単に言えば、次のことを意味します。

ベースまたはメンバー サブオブジェクトの構築が失敗した場合、オブジェクト全体の構築が失敗する必要があります。

于 2012-10-25T12:31:57.403 に答える
2

違いは、次の末尾にあります。

A()
try
{ 
  throw 4;
}
catch(...)
{
  cout << "Catch in A()\n";
}

例外は暗黙的に再スローAされ、オブジェクトは構築されませんが、次の場合:

A(int) {
try
{ 
  throw 4;
}
catch(...)
{
  cout << "Catch in A(int)\n";
}
}

例外を飲み込み、のインスタンスAが完全に構築されます。

デストラクターは、完全に構築されたオブジェクト (コンストラクターが正常に終了したオブジェクト) でのみ実行され、例外はスローされません。

編集: サブオブジェクトの破壊に従って、catch最初のケースでは、サブオブジェクトが破壊された後に実行されます。これは、実際に起こるべきことを示唆するメンバー初期化構文と一致しています。

A()
try : ob() // default construct
{ 
  throw 4;
}
catch(...)
{
  // here ob is already destructed
  cout << "Catch in A()\n";
}

(最初のケースに相当します。)

于 2012-10-25T12:31:01.577 に答える
1

サブオブジェクトの破壊の順序に違いがあるのはなぜですか?

一般に、 の関数 catch 句ではA()、コンストラクターの 1 つから例外が発生した可能性があるため、どのメンバーが正常に構築されたかはわかりません。したがって、疑念を取り除くために、それらは最初に破壊されます。基本的に、関数 try/catch はデータ メンバー構造の「外側」にあります。

A() でスローされた例外が main() に伝播するのはなぜですか?

関数の catch 句では、コンストラクターを成功させることはできません (そのメンバーが正常に構築されなかった場合、オブジェクト自体も正常に構築されていないため)。したがって、それ以外のものをスローしない場合、元の例外が再スローされます。それが定義されている方法です.function-try句を使用して問題を無視することはできません. 関数内で通常の try/catch を使用して問題を無視することができます。問題が原因でオブジェクトが正しく構築されていないかどうかを判断するのはあなた次第です。

于 2012-10-25T12:32:02.123 に答える