3

以前、C ++で例外をチェーンする方法について質問しましたが、その回答の1つは、それを実行する方法に対する気の利いた解決策を提供しました。問題は、私がコードを理解していないことです。コメントでこの種の議論をしようとするのは、あまりにも面倒です。だから私は完全に新しい質問を始める方が良いと思いました。

コードは以下に含まれており、取得できない各セクションに明確にマークを付けています。私が理解していないことの説明は、コードの下に含まれています。コードはPotatoswatterによって書かれました。


コード


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!

わからないこと

  1. throw;ブロック内tryこれは今まで見たことがありません。throw;私が有効だと思った唯一の場所は、catch捕らえられたものを投げ直すためのブロックの内側でした。では、これは何をするのでしょうか?一部のデバッグでは、スローされた例外が以前にスローされたものであることが明らかに示されていますが、それは完全に異なるtryブロック内にありました。struct実際、それは宣言の外でさえありました!

  2. フィールドの交換:なぜ例外フィールドを交換する必要があるのですか?ポインタをコピーするだけで十分ではないでしょうか。これは、フィールドが指す構造がヒープから時期尚早に削除されるのを防ぐためですか?

  3. チェックlink link'sリンク:linkそうでないチェックは理解できますが(ポインタをNULL削除しても効果はありませんが)、なぜ'sリンクをチェックする必要があるのでしょうか。NULLlink

  4. ダミー例外をスローする:なぜこのダミーが必要なのですか?投げられたが、その後落とされた。チェーンの終わりとしてこれが必要なのはなぜですか?

4

1 に答える 1

4

巧妙なコード-これについてのpotatoswatterへの称賛。でも、最後のアイテムを回避する方法を見つけなければならないと思います。

  1. throw;アクティブな例外を再スローします。catchブロックがスタック上にある場合にのみ有効です。どこでその一口に出くわしたのか思い出せませんが、他の質問の文脈ではおそらくSOにありました。ベアスローは、chained_exceptionコンストラクターで現在の例外をキャッチすることにより、現在の例外にアクセスできるようにします。つまり、prevコンストラクターには、現在処理している例外への参照があります。

  2. あなたはここで正しいです。これにより、二重削除が防止されます。

  3. スローされたセンチネル例外は、main決して削除しないでください。この例外の1つの識別属性は、そのlinkメンバーがであるということですNULL

  4. これは私が好きではない部分ですが、簡単な方法を考えることはできません。表示される唯一のchained_exceptionコンストラクターは、catchブロックがアクティブな場合にのみ呼び出すことができます。IIRC、アクティブなcatchブロックのないベアスローはノーノーです。したがって、回避策は、mainすべてのコードをcatchブロックに入れて配置することです。

ここで、このメソッドをマルチスレッドコードで試す場合は、(4)をよく理解していることを確認してください。これをスレッドエントリポイントに複製する必要があります。

于 2010-08-25T14:44:01.077 に答える