2014-11-12 7 views
56

Ich habe eine Funktion foo, die eine bar Ausnahme auslösen kann.Mit `throw;` auf einer modifizierten Ausnahme

In einer anderen Funktion rufe ich foo, aber ich habe die Möglichkeit, mehr Details zur bar Ausnahme hinzuzufügen, wenn sie ausgelöst wird. (. Ich möchte lieber nicht solche Informationen als Parameter an foo passieren, da es nicht wirklich da gehört aufgrund der generischen Art dieser Funktion)

So mache ich das in dem Anrufer:

try { 
    foo(); 
} catch (bar& ex){ 
    ex.addSomeMoreInformation(...); 
    throw; 
} 

Will throw die modifizierte Ausnahme erneut werfen oder muss ich throw ex; verwenden? Letzteres würde vermutlich eine Wertkopie nehmen, also würde ich das lieber nicht machen. Würde throw einen Wert kopieren? Ich vermute es würde nicht.

(Ich bin mir bewusst, dass ich überprüfen konnte, aber ich bin besorgt über Stolpern auf ein nicht näher spezifiziertes oder undefiniertes Konstrukt, so würde ich gerne wissen).

Antwort

27

C++ 11 § 15.1/8:

A throw-expression ohne Operanden rethrows die aktuell behandelte Ausnahme (15.3). Die Ausnahme ist reaktiviert mit dem vorhandenen temporären; Es wird kein neues temporäres Ausnahmeobjekt erstellt.

+15

Bitte fügen Sie eine Erklärung in nicht standardisierter Form hinzu. So verstehe ich die Antwort nicht einmal. Wird die Änderung wirksam oder nicht? – marczellm

+3

@marczellm "keine neue temporäre Ausnahme Objekt erstellt wird" – Theolodis

+0

@anonymous downvoter: bitte erklären Sie Ihren Downvote, es ist verwirrend. –

57

Eigentlich ist der Standard hier sehr genau. [Except.handle]/17:

Wenn der Handler einen Verweis auf ein nicht konstantes Objekt erklärt, alle Änderungen auf das referenzierte Objekt sind Änderungen an das temporären Objekt initialisiert, wenn der throw-Ausdruck wurde ausgeführt und wird Wirkung haben, sollte das Objekt erneut ausgelöst werden.

Und [except.throw]/8:

A throw-Ausdruck ohne Operanden rethrows die derzeit behandelt Ausnahme (15.3).

+0

Dies sollte die akzeptierte Antwort sein. –

3

throw (ohne ein Ausnahmeobjekt) wird die aktuelle Ausnahme erneut auslösen. (muss innerhalb catch block sein, sonst wird std::terminate aufgerufen). Da Sie die Referenz des aktuellen Ausnahmeobjekts geändert haben, müssen Sie das Objekt nicht explizit werfen und throw werfen die geänderte Ausnahme erneut und kein neues temporäres Objekt wird erstellt.

+3

Richtig, das weiß er. Du hast die Frage nicht beantwortet. –

+2

@LightnessRacesinOrbit 'Sie brauchen nicht explizit das Objekt zu werfen' ist die Antwort, die ich glaube. Vorher ist der Grund warum. Ich nehme an, das war etwas, das du nicht magst und das du abgelehnt wurdest. –

+0

Kein Kumpel, das ist _nicht_ die Antwort. Das OP möchte Informationen dazu, was 'throw' bewirkt, wenn das Ausnahmeobjekt modifiziert wurde. Ihre Antwort kommt nicht annähernd dazu, das zu erklären. Es sagt nur "es wird die aktuelle Ausnahme wieder auflösen" - ja, das OP weiß das schon. Er versucht, genauere Informationen darüber zu bekommen, wie das funktioniert. Keine Notwendigkeit, so defensiv zu werden. –

3

In diesem Fall sollten Sie throw verwenden, um das gewünschte Verhalten zu erhalten ... d. H. Throw würde die modifizierte Ausnahme auslösen, da die Ausnahme durch Referenz abgefangen wurde.

Lassen Sie mich versuchen Unterschied zwischen diesen Wurf explizit durch Beispiele zu machen: -

class exception 
{ 
}; 

class MyException : public exception 
{ 
}; 

void func() 
{ 
    try 
    { 
    throw MyException(); 
    } 
    catch(exception& e) 
    { 
    //do some modification. 
    throw;     //Statement_1 
    throw e;     //Statement_2 
    } 
} 

Statment_1: -

Was Wurf tut, ist es wirft wieder genau das, was die aktuelle Ausnahme ist, dh es doesn t weitere Kopien erstellen (wie bei der ersten Ausnahmebedingung). Wenn Sie also hier an der abgefangenen Ausnahme Änderungen vornehmen ... wäre dies auch in der Aufrufroutine der Fall.

Statement_2: -

Dies ist die „Ausnahme“ zu werfen, die ursprünglich gefangen wurde als MyException heißt es die Kopie machen würde wieder. Vergiss also die Änderungen, die du gemacht hast, es wird nicht passieren oder eine Ausnahme für den Anrufer. Es wirft "Ausnahme" auf die Aufrufroutine.

Hoffnung Ich bin klar (und auf dem richtigen Weg von C++ STANDARD) genug ...

+0

Ich sehe nicht, wie "throw e" magisch "Änderungen, die du gemacht hast" vergisst. Die Kopie ist möglicherweise nicht wünschenswert, aber Sie kopieren immer noch das Ausnahmeobjekt _ nachdem Sie es geändert haben_. –

+0

Ausnahmen werden mit ihrem statischen Typ anstelle des dynamischen Typs kopiert ... – ravi

+1

Oh, das ist ein Problem. Okay. Die Tatsache, dass das Slicing durchgeführt wird, ist jedoch nicht dasselbe wie zu sagen, dass die Änderungen des OP verloren gehen: Sie werden es nicht tun. –

2

nach this, Ausnahmen in c werfen ++ kann auf zwei Arten erfolgen:

  1. Wurfexpression: Zuerst kopiert copy-initialisiert das Exception-Objekt von expression (dies kann den move-Konstruktor für rvalue-Ausdruck aufrufen, und die Kopie/Verschiebung kann kopiert werden) und überträgt dann die Kontrolle an den Exception-Handler mit dem passenden Typ, dessen Compound Aussage oder Die Mitgliedsinitialisierungsliste wurde zuletzt eingegeben und nicht von diesem Ausführungsstrang verlassen.
  2. throw: Löst die aktuell behandelte Ausnahme aus. Verlässt die Ausführung des aktuellen catch-Blocks und übergibt die Steuerung an den nächsten passenden Ausnahmebehandler (aber nicht an eine andere catch-Klausel nach demselben try-Block: seine zusammengesetzte Anweisung wird als 'exited' betrachtet), wobei das vorhandene Ausnahmeobjekt wiederverwendet wird: Es werden keine neuen Objekte erstellt. Dieses Formular ist nur erlaubt, wenn gerade eine Exception behandelt wird (es ruft std :: terminate auf, wenn es anderweitig verwendet wird). Die catch-Klausel, die einem Funktionstry-Block zugeordnet ist, muss durch erneutes Auswerfen beendet werden, wenn sie in einem Konstruktor verwendet wird.

Um meine Antwort zu unterstreichen, werfen sollte in Ihrem Fall in Ordnung sein.