破壊されたオブジェクトの使用は未定義の動作です。これは、あなたが今この振る舞いをしているかもしれないことを意味します、しかしあなたがいつかそれを得るという保証は何もありません。この例が示すように、未定義の動作は検出がより困難になる可能性があるため、通常のバグよりも危険です。
更新:いくつかのコメントに続いて、OPのコードがその出力を生成する理由を説明します。
try
関数ブロックは、C++ではややあいまいな機能です。関数の本体全体try
を、対応する。で囲むことができcatch
ます。これは、代わりに:
void foo()
{
try
{
//...
}
catch (/*whatever*/)
{
//...
}
}
あなたは書ける:
void foo()
try
{
//...
}
catch (/*whatever*/)
{
//...
}
これは実際にはあまり有用ではありませんが、try
ブロック内に初期化リストを含める唯一の方法であるため、コンストラクターにとってはわずかに有用です。したがって、A
コンストラクターのOPのコードは次のようになります。
A()
try
: b()
{
throw 4;
}
catch(...)
{
cout << "Catched in A() handler : ob.b= " << ob.b<<endl;
}
catch
ブロック内で作成しているオブジェクトを使用できないため、これはほんのわずかに役立つと言いました。ブロック内にスローするtry
と、例外はコンストラクター本体を離れるため、オブジェクトは構築されておらず、構築されたデータメンバーはcatch
ブロックに入る前にすぐに破棄されます。ただし、ロギングの目的で使用できる場合があります。
さて、ご存知のとおり、コンストラクター(nothrow
バージョンを使用していないと仮定)は2つのことしか実行できません。
- 構築されたオブジェクトを返す
- 例外をスローする
このコンストラクターではスローしますが、例外はcatch
ブロックでキャッチされます。では、今何が起こっているのでしょうか?コンストラクターを呼び出すコードには何が返されますか?構築されたオブジェクトがないため、オブジェクトを返すことはできません。そのため、選択肢は1つだけです。つまり、catch
ブロックをスローする必要があります。そして、これは実際にはこの場合の標準で義務付けられているものです。明示的にスローしない場合、コンパイラはブロックthrow;
の最後に命令をサイレントに追加します。catch
したがって、もう少し詳しく説明すると、コンストラクターは次のようになります。
A()
try
: b()
{
throw 4;
}
catch(...)
{
cout << "Catched in A() handler : ob.b= " << ob.b<<endl;
throw;
}
そして、これが例外が2回キャッチされる理由です。1回はA
コンストラクターで、もう1回はmain()
。