Frage

Im folgenden Code wird die stackbasierte Variable 'Ex' in eine Funktion geworfen und über den Bereich, in dem Ex deklariert wurde, gefangen. Dies scheint mir etwas seltsam, da (AFAIK) -Stapel-basierte Variablen nicht außerhalb des Zielfernrohrs verwendet werden können, in dem sie deklariert wurden (der Stapel ist abgewickelt).

void f() {
    SomeKindOfException ex(...);
    throw ex;
}

void g() {
    try {
        f();
    } catch (SomeKindOfException& ex) {
        //Handling code...
    }
}

Ich habe eine Print -Erklärung zu dem Destruktor von SongindoFexception hinzugefügt und sie zeigt, dass Ex zerstört wird, sobald er in F () den Spielraum herausgibt, aber dann ist es in g () gefangen und wieder zerstört, sobald es dort auch aus dem Spielraum ausgeht.

Irgendeine Hilfe?

War es hilfreich?

Lösung

Das Ausnahmeobjekt wird an einen besonderen Ort kopiert, um den Stapelabwickeln zu überleben. Der Grund, warum Sie zwei Zerstörungen sehen, ist, dass beim Beenden f () die ursprüngliche Ausnahme zerstört wird und wenn Sie g () die Kopie verlassen.

Andere Tipps

Das Objekt wird in eine kopiert Ausnahmeobjekt Das überlebt Stack-Unwinding. Woher der Speicher für dieses Objekt kommt, ist nicht spezifiziert. Für ein großes Objekt wird es wahrscheinlich sein malloc'ed, und für kleinere Objekte könnte die Implementierung einen voranallozierten Puffer haben (ich könnte mir vorstellen, dass dies für a verwendet werden könnte bad_alloc Ausnahme).

Die Referenz ex ist dann daran gebunden Ausnahmeobjekt, was vorübergehend ist (es hat keinen Namen).

C ++ Standard 15.1/4:

Der Speicher für die temporäre Kopie der Ausnahme, die ausgelöst wird, wird auf nicht spezifizierte Weise zugewiesen, außer wie in 3.7.3.1 angegeben. Der vorübergehende Bestehen bleibt bestehen, solange ein Handler für diese Ausnahme ausgeführt wird. Insbesondere wenn ein Handler durch Ausführen eines Wurfs verlässt; Aussage, die die Kontrolle an einen anderen Handler für die gleiche Ausnahme übergibt, so bleibt die vorübergehenden Überreste. Wenn der letzte Handler, der für die Ausnahme ausgeführt wird, mit anderen Mitteln als Wurf ausgeht; Das temporäre Objekt wird zerstört und die Implementierung kann den Speicher für das temporäre Objekt bearbeiten. Eine solche Deallokation erfolgt nicht spezifiziert. Die Zerstörung erfolgt unmittelbar nach der Zerstörung des Objekts, die in der Ausnahmedeklaration im Handler deklariert wurde.

Es gibt nichts mehr zu sagen.

Wenn Sie Ex werfen, wird es an einem speziellen Speicherort kopiert, der für geworfene Ausnahmeobjekte verwendet wird. Eine solche Kopie wird vom normalen Kopierkonstruktor durchgeführt.

Sie können dies leicht aus diesem Beispiel sehen:

#include <iostream>

void ThrowIt();

class TestException
{
  public:
    TestException()
    {
        std::cerr<<this<<" - inside default constructor"<<std::endl;
    }

    TestException(const TestException & Right)
    {
        (void)Right;
        std::cerr<<this<<" - inside copy constructor"<<std::endl;
    }

    ~TestException()
    {
        std::cerr<<this<<" - inside destructor"<<std::endl;    
    }
};

int main()
{
    try
    {
        ThrowIt();
    }
    catch(TestException & ex)
    {
        std::cout<<"Caught exception ("<<&ex<<")"<<std::endl;
    }
    return 0;
}

void ThrowIt()
{
    TestException ex;
    throw ex;
}

Beispielausgabe:

matteo@teolapubuntu:~/cpp/test$ g++ -O3 -Wall -Wextra -ansi -pedantic ExceptionStack.cpp -o ExceptionStack.x
matteo@teolapubuntu:~/cpp/test$ ./ExceptionStack.x 
0xbf8e202f - inside default constructor
0x9ec0068 - inside copy constructor
0xbf8e202f - inside destructor
Caught exception (0x9ec0068)
0x9ec0068 - inside destructor

Übrigens können Sie hier sehen, dass der für das geworfene Objekt verwendete Speicherort (0x09ec0068) definitiv weit vom ursprünglichen Objekt (0xBF8E202F) entfernt ist Das geworfene Objekt ist im virtuellen Adressraum recht unten. Dennoch ist dies ein Implementierungsdetail, da, wie andere Antworten betonten, der Standard nichts darüber aussagt, wo das Speicher für ein geworfenes Objekt sein sollte und wie es zugewiesen werden sollte.

Zusätzlich zu dem, was der Standard in 15.1/4 sagt ("Ausnahmebehandlung/Werfen einer Ausnahme") -, dass der Speicher für die temporäre Kopie der Ausnahme auf nicht näher bezeichnete Weise zugewiesen wird - ein paar andere Teile von Trivia darüber, wie die Ausnahmeobjekt wird zugewiesen::

  • 3.7.3.1/4 ("Zuweisungsfunktionen") des Standard new Ausdruck oder Aufruf einer 'globalen Zuweisungsfunktion' (dh, an operator new() Ersatz). Beachten Sie, dass malloc() Ist keine "globale Allokationsfunktion", wie vom Standard definiert, also malloc() ist definitiv eine Option zur Zuweisung des Ausnahmebobjekts.

  • "Wenn eine Ausnahme ausgelöst wird, wird das Ausnahmebobjekt erstellt und ein im Allgemeinen auf einem Ausnahmedatenstapel platziert" (Stanley Lippman, "innerhalb des C ++ - Objektmodells" - 7.2 Ausnahmebehandlung)

  • Aus StrouStrups "The C ++ Programmiersprache, 3rd Edition": "Eine C ++ - Implementierung ist erforderlich, um genügend Ersatzspeicher zu haben, um zu werfen bad_alloc Im Falle einer Gedächtniserschöpfung. Es ist jedoch möglich, dass die Erschöpfung der Gedächtniserschöpfung zu einer Gedächtniserschöpfung führt. Es ist jedoch garantiert, dass ausreichend Speicher vorhanden ist, um zuzulassen new die Standardausnahme außerhalb des Memorys auswerfen, bad_alloc"(14.3 Ausnahmen erfassen).

Beachten Sie, dass die Zitate von Stroustrup vor dem Standard sind. Ich finde es interessant, dass der Standard nicht die Garantie zu machen scheint, dass Strousstrup für wichtig genug war, um zweimal zu erwähnen.

Weil die Spezifikation ausdrücklich feststellt, dass ein vorübergehendes Objekt anstelle des throw Operand.

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