単純な用語へのあいまいな翻訳は次のようになります: function-try-blocks は例外を変換するためにのみ使用でき、常に RAII を使用し、各リソースは単一のオブジェクトによって管理される必要があり、それらは矛盾しません。まあ、翻訳は正確にはそうではありませんが、議論は最終的にこれら2つの結論につながります.
C++FAQ lite からの 2 番目の引用では、コンストラクターが完了していないオブジェクトに対してはデストラクタが呼び出されないことが示されています。これは、オブジェクトがリソースを管理している場合、さらに複数のリソースを管理している場合は、深刻な問題に直面していることを意味します。コンストラクターをエスケープする前に例外をキャッチし、取得したリソースを解放しようとすることができますが、そのためには実際に割り当てられたリソースを知る必要があります。
最初の引用は、コンストラクター内の関数 try ブロックがスロー (または再スロー) する必要があることを示しているため、その有用性は非常に制限されており、特に唯一の有用なことは、例外を変換することです。関数 try ブロックの唯一の理由は、初期化リストの実行中に例外をキャッチすることであることに注意してください。
しかし待ってください、関数 try ブロックは最初の問題を処理する方法ではないのでしょうか?
ええと...そうではありません。2 つのポインターを持ち、それらにメモリを格納するクラスを考えてみましょう。そして、2 番目の呼び出しが失敗する可能性があることを考慮してください。その場合、最初のブロックを解放する必要があります。関数の try ブロックを使用するか、通常の try ブロックを使用して、2 つの異なる方法で実装を試みることができます。
// regular try block // function try block
struct test {
type * p;
type * q;
test() : p(), q() { test() try : p( new int ), q( new int ) {
try {
p = new type;
q = new type;
} catch (...) { } catch (...) {
delete p; delete p;
throw;
} } // exception is rethrown here
}
~test() { delete p; delete q; }
};
最初に単純なケースを分析できます: 通常の try ブロックです。最初のコンストラクターは、2 つのポインターを null (: p(), q()初期化リスト内) に初期化し、両方のオブジェクトのメモリを作成しようとします。2 つのうちの 1 つでnew type例外がスローされ、catch ブロックに入ります。何newが失敗しましたか?失敗したのが 2 番目の new であったとしても、それdeleteは実際にリリースされpます。それが最初のものであった場合、初期化リストは最初に両方のポインターを設定し、null ポインターで0安全に呼び出すことができるため、最初の new が失敗した場合でも安全な操作です。deletedelete p
右の例を見てみましょう。リソースの割り当てを初期化リストに移動したため、例外をキャプチャする唯一の方法である関数 try ブロックを使用します。繰り返しますが、ニュースの 1 つが失敗します。2 番目の new が失敗した場合、delete pはそのポインタに割り当てられたリソースを解放します。しかし、失敗したのが最初の new だった場合、 はp初期化されておらず、 への呼び出しdelete pは未定義の動作です。
大まかな翻訳に戻ると、RAIIを使用し、オブジェクトごとに 1 つのリソースのみを使用した場合、型は次のように記述されます。
struct test {
std::auto_ptr<type> p,q;
test() : p( new type ), q( new type )
{}
};
この変更された例では、RAII を使用しているため、例外はあまり気にしません。最初のnewスローの場合、リソースは取得されず、何も起こりません。2 番目のスローが失敗した場合、2 番目のスローが試行される前に完全に構築されているpため破棄され(そこにシーケンス ポイントがあります)、リソースは解放されます。pnew
したがって、リソースを管理する必要さえありませんtry...これにより、Sutter が言及した他の使用法、つまり例外の変換が残ります。失敗したコンストラクターからスローする必要がありますが、スローするものを選択できます。initialization_error構造上の内部障害が何であったかに関係なく、カスタムメイドをスローしたいと判断する場合があります。これが、関数 try ブロックを使用できる目的です。
struct test {
std::auto_ptr<type> p,q;
test() try : p( new type ), q( new type ) {
} catch ( ... ) {
throw initialization_error();
}
};