Frage

Ich fragte vorher eine Frage darüber, wie Verkettungs Ausnahmen in C ++, und eine der Antworten vorgesehen, um eine raffinierte Lösung, wie es getan werden kann. Das Problem ist, dass ich den Code nicht verstehen, und diese Art der Diskussion in den Kommentaren zu haben versucht, ist einfach zu viel zuviel Mühe. Also ich dachte, es ist besser, völlig eine neue Frage zu beginnen.

Der Code wird auch weiter unten, und ich habe jeden Abschnitt eindeutig gekennzeichnet, die ich nicht bekommen. Eine Beschreibung von dem, was ich nicht verstehe, ist unter dem Code enthält. Der Code wurde geschrieben von Potatoswatter .


Code


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();
}

Ausführen des Codes gibt die folgende Ausgabe:

bah
humbug!
meh!

Was ich nicht verstehe

  1. throw; innerhalb try-Block: Ich habe das noch nie gesehen. Der einzige Ort, wo ich gedacht habe throw; gültig zu sein war in einem catch-Block erneut auslösen, was gefangen wurde. Was bedeutet das? Einige Debuggen offenbar zeigt, dass die ausgelöste Ausnahme ist das, was vorher geworfen wurde, aber das war in einem ganz anderen try-Block. In der Tat, es war auch außerhalb der struct Erklärung!

  2. Swap Felder: Warum brauchen wir auf Swap die Ausnahmefelder? Wäre das nicht nur das Kopieren des Zeigers genug sein? Ist das die Strukturen zu verhindern, der die Felder am Punkt von aus dem Heap vorzeitig gelöscht werden?

  3. Überprüfen link und link den Link: Ich verstehe, kann prüfen, ob link nicht NULL ist (obwohl ein NULL Zeiger zu löschen hat keine Auswirkungen), aber warum die Notwendigkeit, die link die Verbindung zu überprüfen?

  4. Werfen Dummy Ausnahme: Warum wird diese Dummy benötigt? Es wird geworfen, aber dann fallen gelassen. Warum brauchen wir das als ein Ende der Kette?

War es hilfreich?

Lösung

Clever-Code - ein dickes Lob an potatoswatter auf diesem. Ich denke, dass ich müsste allerdings eine gewisse Art und Weise um das letzte Element finden.

  1. throw; rethrows die aktive Ausnahme. Es ist nur gültig, wenn ein catch Block auf dem Stapel befindet. Ich kann mich nicht erinnern, wo ich auf diesem tidbit kam, aber es war wahrscheinlich auf SO im Zusammenhang mit einer anderen Frage. Der bloße Wurf gibt uns auf die aktuelle Ausnahme zugreifen, indem sie in der chained_exception Konstruktor zu kontrollieren. Mit anderen Worten, prev im Konstruktor ist ein Verweis auf die Ausnahme, dass wir gerade bearbeiten.

  2. Sie sind hier richtig. Dies verhindert, dass doppelte Streichung.

  3. Die Sentinel-Ausnahme, die man in main geworfen, sollte nie gelöscht werden. Das ein Identitätsmerkmal dieser Ausnahme ist, dass es link Mitglied ist NULL.

  4. Dies ist der Teil, dass ich nicht wie, aber kann nicht denken eine einfache Möglichkeit, um. Der einzige sichtbare chained_exception Konstruktor kann nur aufgerufen werden, wenn ein catch Block aktiv ist. IIRC, ein nackter Wurf ohne aktiven catch Block ist ein No-No. Also, die Abhilfe ist in main zu werfen und die gesamten Code in dem catch Block setzen.

Nun, wenn Sie diese Methode in Multi-Threaded-Code versuchen, stellen Sie sicher, dass Sie verstehen, (4) sehr gut. Sie wird diesen Punkt in der Thread-Eintrag replizieren.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top