このチェーン例外のこの実装はどのように機能しますか?
-
01-10-2019 - |
質問
私は以前に尋ねました 質問 C ++で例外をチェーンする方法について、および回答の1つは、それがどのように行われるかについて気の利いたソリューションを提供しました。問題は、私がコードを理解していないことであり、コメントでこの種の議論をしようとすることはあまりにも面倒です。だから、新しい質問を完全に開始する方が良いと思いました。
コードは以下に含まれており、私が取得していない各セクションを明確にマークしました。私が理解していないことの説明は、コードの下に含まれています。コードはによって書かれました ポタトスワッター.
コード
struct exception_data { // abstract base class; may contain anything
virtual ~exception_data() {}
};
struct chained_exception : std::exception {
chained_exception( std::string const &s, exception_data *d = NULL )
: data(d), descr(s) {
try {
link = new chained_exception;
// ----------------------------------------------------------------
// How does this work (section 1)?
throw;
// ----------------------------------------------------------------
} catch ( chained_exception &prev ) {
// ----------------------------------------------------------------
// How does this work (section 2)?
swap( *link, prev );
// ----------------------------------------------------------------
} // catch std::bad_alloc somehow...
}
friend void swap( chained_exception &lhs, chained_exception &rhs ) {
std::swap( lhs.link, rhs.link );
std::swap( lhs.data, rhs.data );
swap( lhs.descr, rhs.descr );
}
virtual char const *what() const throw() { return descr.c_str(); }
virtual ~chained_exception() throw() {
// --------------------------------------------------------------------
// How does this work (section 3)?
if ( link && link->link ) delete link; // do not delete terminator
// --------------------------------------------------------------------
delete data;
}
chained_exception *link; // always on heap
exception_data *data; // always on heap
std::string descr; // keeps data on heap
private:
chained_exception() : link(), data() {}
friend int main();
};
void f() {
try {
throw chained_exception( "humbug!" );
} catch ( std::exception & ) {
try {
throw chained_exception( "bah" );
} catch ( chained_exception &e ) {
chained_exception *ep = &e;
for ( chained_exception *ep = &e; ep->link; ep = ep->link ) {
// Print ep->what() to std::cerr
}
}
}
try {
throw chained_exception( "meh!" );
} catch ( chained_exception &e ) {
for ( chained_exception *ep = &e; ep->link; ep = ep->link ) {
// Print ep->what() to std::cerr
}
}
}
int main() try {
// ------------------------------------------------------------------------
// How does this work (section 4)?
throw chained_exception(); // create dummy end-of-chain
// ------------------------------------------------------------------------
} catch( chained_exception & ) {
// body of main goes here
f();
}
コードを実行すると、次の出力が得られます。
bah
humbug!
meh!
私が理解していないこと
throw;
中身try
-ブロック: 私はこれを見たことがありません。私が思った唯一の場所throw;
有効であることは内部にありましたcatch
- キャッチされたものをrethrowにブロックします。それで、これは何をしますか?いくつかのデバッグは明らかに、スローされた例外が以前に投げられたものであることを示していますが、それはまったく異なる内部でしたtry
-ブロック。実際、それは外にさえありましたstruct
宣言!フィールドを交換: なぜ必要とするのか スワップ 例外フィールド?ポインターをコピーするだけで十分ではないでしょうか?これは、フィールドがヒープから早期に削除されるのを指す構造を防ぐためですか?
小切手
link
とlink
'のリンク: 私はそれをチェックすることを理解できますlink
ではありませんNULL
(削除してもNULL
ポインターには効果がありません)、なぜチェックする必要があるのかlink
リンク?ダミー例外をスロー: なぜこのダミーが必要なのですか?投げられますが、落とされました。なぜこれをチェーンの終わりとして必要とするのですか?
解決
賢いコード - これについてのポタトスワッターへの称賛。しかし、私は最後のアイテムの周りに何らかの方法を見つけなければならないと思います。
throw;
アクティブな例外をrethrowsします。 aの場合にのみ有効ですcatch
ブロックはスタック上にあります。私はそのちょっとしたことに出会った場所を思い出せませんが、おそらく他の質問の文脈ではそうでした。裸のスローは、現在の例外をキャッチすることで現在の例外にアクセスできますchained_exception
コンストラクタ。言い換えると、prev
コンストラクターには、現在処理している例外への参照があります。あなたはここで正しいです。これにより、二重の削除が防止されます。
センチネルの例外、投げ込まれたもの
main
, 、削除されないでください。この例外の識別属性は、それがlink
メンバーはですNULL
.これは私が好きではないが、簡単な方法を考えることはできない部分です。唯一の目に見える
chained_exception
コンストラクターは、aの場合にのみ呼び出すことができますcatch
ブロックはアクティブです。 IIRC、アクティブなしで裸のスローcatch
ブロックはノーです。したがって、回避策は投入することですmain
すべてのコードをに入れますcatch
ブロック。
ここで、この方法をマルチスレッドコードで試してみると、(4)を非常によく理解していることを確認してください。これをスレッドエントリポイントに複製する必要があります。