2009-09-26 9 views
19

Stellen Sie sich zwei ähnliche Stücke von Code:Was ist der Unterschied zwischen werfen und werfen mit Arg gefangener Ausnahme?

try { 
    [...] 
} catch (myErr &err) { 
    err.append("More info added to error..."); 
    throw err; 
} 

und

try { 
    [...] 
} catch (myErr &err) { 
    err.append("More info added to error..."); 
    throw; 
} 

Sind diese effektiv gleich oder unterscheiden sie sich in irgendeiner subtile Art und Weise? Zum Beispiel bewirkt die erste, dass ein Kopierkonstruktor ausgeführt wird, während vielleicht die zweite das gleiche Objekt wiederverwendet, um es erneut zu werfen?

Antwort

26

Je nachdem, wie Sie Ihre Ausnahme-Hierarchie, Wieder Auslösen einer Ausnahme durch die Benennung der Ausnahmevariable in der throw-Anweisung angeordnet haben kann Scheibe die ursprüngliche Ausnahmeobjekt.

A no-Argument throw Ausdruck wird die aktuelle Ausnahmeobjekt Erhaltung seiner dynamischen Typ werfen, während ein throw Ausdruck mit einem Argument, wird eine neue Ausnahme auf der Grundlage der statischen Typ des Arguments throw werfen.

z.

int main() 
{ 
    try 
    { 
     try 
     { 
      throw Derived(); 
     } 
     catch (Base& b) 
     { 
      std::cout << "Caught a reference to base\n"; 
      b.print(std::cout); 
      throw b; 
     } 
    } 
    catch (Base& b) 
    { 
     std::cout << "Caught a reference to base\n"; 
     b.print(std::cout); 
    } 

    return 0; 
} 

Wie oben geschrieben, das Programm folgende Ausgabe:

Caught a reference to base 
Derived 
Caught a reference to base 
Base

Wenn die throw b ersetzen ist mit einem throw, dann wird die äußere Fang wird auch die ursprünglich geworfen Derived Ausnahme fangen. Dies gilt immer noch, wenn die innere Klasse die Base Ausnahme nach Wert anstatt nach Referenz abfängt - obwohl dies natürlich bedeuten würde, dass das ursprüngliche Ausnahmeobjekt nicht geändert werden kann. Daher würden sich Änderungen an b nicht in der Derived Ausnahme des äußeren Blocks widerspiegeln .

+1

Ah, ich habe das Schneiden völlig vergessen! Verdammt, das ist wichtig! Danke, dass du das angesprochen hast. +1 (Obwohl ich glaube, als Sie geschrieben haben "... den ursprünglichen statischen Typ zu bewahren ..." meinten Sie _dynamic_ type. Was heißt _dynamic type_, wenn nicht der _ "original static type _ _." - – sbi

+1

Großartig Antwort, das habe ich auch komplett vergessen. – GManNickG

+0

Ich bin glücklich, dass jemand anderes in das _slicing_ Problem gelaufen ist;) –

16

Im zweiten Fall gemäß Standard-C++ ist 15,1/6 Copykonstruktor nicht verwendet:

einen Einwurf Ausdruck ohne Operand rethrows die Ausnahme behandelt wurde. Die Ausnahme wird mit dem vorhandenen temporären reaktiviert; Es wird kein neues temporäres Ausnahmeobjekt erstellt. Die Ausnahme gilt nicht mehr als abgefangen; Daher ist der Wert von uncaught_exception() wieder wahr.

der Typ davon

ein Einwurf Ausdruck initialisiert durch Entfernen ein temporäres Objekt, genannt, das Ausnahmeobjekt jeden bestimmt wird:

Im ersten Fall neue Ausnahme wird nach 15,1/3 geworfen cv-qualifikatoren auf oberster Ebene vom statischen Typ des Operanden throw and adjusting der Typ von "Array von T" oder "Funktion, die T zurückgibt" auf "Zeiger auf T" oder "Zeiger auf Funktion, die T zurückgibt", . < ...> Das temporäre wird verwendet, um die Variable zu initialisieren, die im passenden Handler (15.3) benannt ist. Der Typ des throw-Ausdrucks darf kein unvollständiger Typ oder ein Zeiger oder Verweis auf einen unvollständigen Typ sein, mit Ausnahme von void *, const void *, flüchtiger void * oder const volatile void *. Mit Ausnahme dieser Einschränkungen und der in 15.3 erwähnten Einschränkungen für die Typübereinstimmung wird der Operand von throw in einem Aufruf (5.2.2) oder dem Operanden einer return-Anweisung genau wie ein Funktionsargument behandelt.

Wenn das geworfene Objekt ein Klassenobjekt ist, und der Copykonstruktor verwendet, um die temporäre Kopie zu initialisieren nicht zugänglich ist:

In beiden Fällen Copy-Konstruktor wird in throw Stufe (15,1/5) erforderlich , das Programm ist schlecht ausgebildet (selbst wenn das temporäre Objekt sonst eliminiert werden könnte). Wenn das Destruktor für das Objekt nicht verfügbar ist, ist das Programm ähnlich schlecht (selbst wenn das temporäre Objekt andernfalls beseitigt werden könnte).

+0

Da die Kopie ctor für die ursprünglich geworfene Ausnahme sowieso zugänglich sein muss, denke ich, dass das in diesem Fall kein Problem ist. Aber deine ist immer noch eine gute Antwort. +1 – sbi

+0

Ja, aber im ersten Fall wird copy-tor zweimal verwendet. –

+1

Es wird angegeben, wenn die Deklaration einen Klassentyp angibt. In diesem Fall wird jedoch ein Referenztyp angegeben. Der Verweis wird direkt gebunden und verweist auf das Ausnahmeobjekt. Also werden wir eine Kopie ctor nur zum Zeitpunkt des Würfelns benötigen, in diesem Fall nicht fangen (es würde einen Unterschied machen, wenn beispielsweise ein Basiskopierkonstruktor geschützt ist). –