破壊されたオブジェクトの使用は未定義の動作です。これは、あなたが今この振る舞いをしているかもしれないことを意味します、しかしあなたがいつかそれを得るという保証は何もありません。この例が示すように、未定義の動作は検出がより困難になる可能性があるため、通常のバグよりも危険です。
更新:いくつかのコメントに続いて、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()。