Ist die C ++ - Fangklausel, Familien von Ausnahmeklassen und die Vernichtung sinnvoll?

7

Ab und zu bemerke ich ein Kodierungsmuster, das ich seit Jahren habe und es macht mich nervös. Ich habe kein spezifisches Problem, aber ich erinnere mich auch nicht genug daran, warum ich dieses Muster angenommen habe, und irgendein Aspekt scheint mit einigen Anti-Mustern übereinzustimmen. Dies ist mir kürzlich passiert, WRT wie einige meiner Code Ausnahmen verwendet.

Die besorgniserregende Sache betrifft Fälle, in denen ich eine Ausnahme "durch Referenz" erhalte und sie auf ähnliche Weise behandle, wie ich einen Parameter für eine Funktion behandeln würde. Ein Grund dafür ist, dass ich eine Vererbungshierarchie von Ausnahmeklassen haben kann und abhängig von der Anwendung einen allgemeineren oder präziseren Auffangtyp angeben kann. Zum Beispiel könnte ich definieren ...

%Vor%

Das beunruhigt mich jetzt, weil ich bemerkt habe, dass eine Klasseninstanz (als Teil des throw) innerhalb einer Funktion konstruiert wird, aber nach dem Beenden dieser Funktion (über den p_Exception catch-clause "Parameter") referenziert wird. Dies ist normalerweise ein Anti-Pattern - eine Referenz oder ein Zeiger auf eine lokale Variable oder temporär innerhalb einer Funktion erstellt, aber ausgegeben, wenn die Funktion endet, ist normalerweise ein dangling Verweis / Zeiger, da die lokale Variable / temporäre zerstört und der Speicher freigegeben wird wenn die Funktion beendet wird.

Einige schnelle Tests deuten darauf hin, dass der obige throw wahrscheinlich in Ordnung ist - die in der throw-Klausel konstruierte Instanz wird nicht zerstört, wenn die Funktion beendet wird, aber wird zerstört, wenn die catch-Klausel, die sie behandelt, vervollständigt wird Ausnahme, in diesem Fall erledigt der nächste catch-Block diesen Job.

Meine verbleibende Nervosität besteht darin, dass ein Testlauf in ein oder zwei Compilern kein Beweis dafür ist, was der Standard sagt, und da meiner Erfahrung nach der gesunde Menschenverstand oft anders ist, als was die Sprache garantiert.

Also - ist dieses Muster der Behandlung von Ausnahmen (das Abfangen von ihnen unter Verwendung eines Referenztyps) sicher? Oder sollte ich etwas anderes tun, wie ...

  • Abfangen (und explizites Löschen) von Zeigern auf Heap-allokierte Instanzen anstelle von Referenzen auf etwas, das (wenn es geworfen wird) sehr ähnlich aussieht wie ein temporäres?
  • Verwenden Sie eine intelligente Zeigerklasse?
  • Verwenden von "pass-by-value" catch-Klauseln und akzeptieren, dass ich keine Ausnahmeklasse von einer Hierarchie mit einer catch-Klausel abfangen kann?
  • Etwas, an das ich nicht gedacht habe?
Steve314 29.12.2010, 07:53
quelle

5 Antworten

9

Das ist in Ordnung. Es ist eigentlich gut, Ausnahmen durch konstante Referenz (und schlecht zu fangen Zeiger) zu fangen. Wenn Sie nach Wert suchen, wird eine unnötige Kopie erstellt. Der Compiler ist intelligent genug, um die Exception (und deren Zerstörung) richtig zu behandeln - versuchen Sie nicht, die Exception-Referenz außerhalb Ihres catch-Blocks zu verwenden; -)

Tatsächlich tue ich oft meine Hierarchie von std :: runtime_error (die von std :: exception erbt). Dann kann ich .what() verwenden und noch weniger catch-Blöcke verwenden, während ich weitere Ausnahmen behandle.

    
Tim 29.12.2010, 08:00
quelle
6

Dieses Muster ist definitiv sicher.

Es gibt spezielle Regeln, die die Lebensdauer eines geworfenen Objekts verlängern. Effektiv existiert es so lange, wie es behandelt wird, und es ist garantiert, dass es bis zum Ende des letzten catch -Blocks existiert, der es behandelt.

Ein sehr gebräuchliches Idiom ist beispielsweise, benutzerdefinierte Ausnahmen von std::exception abzuleiten, seine what() -Memberfunktion zu überschreiben und sie als Referenz abzufangen, so dass Sie Fehlermeldungen aus einer Vielzahl von Ausnahmen mit einer catch-Klausel ausgeben können .

    
James McNellis 29.12.2010 08:00
quelle
4

Nein, du machst das definitiv richtig. Siehe Ссылка und den Rest des FAQ-Kapitels für diese Angelegenheit.

    
Karl Knechtel 29.12.2010 08:02
quelle
4

Ja. So weit so gut.

Persönlich verwende ich std :: runtime_error als Basis aller Ausnahmefehler. Es behandelt Fehlermeldungen usw.

Geben Sie auch keine weiteren Ausnahmen an, die Sie benötigen. Definieren Sie eine Ausnahme nur für Dinge, die tatsächlich abgefangen und behoben werden können. Verwenden Sie eine allgemeinere Ausnahme für Dinge, die nicht abgefangen oder behoben werden können.

Zum Beispiel: Wenn ich eine Bibliothek A entwickle. Dann habe ich eine von std :: runtime_error abgeleitete AException. Diese Ausnahme wird für alle generischen Ausnahmen aus der Bibliothek verwendet. Für bestimmte Ausnahmen, bei denen der Benutzer der Bibliothek tatsächlich etwas mit der Ausnahme abfangen und tun kann (reparieren oder mildern), erstelle ich eine spezifische Ausnahme, die von einer Ausnahme abgeleitet ist (aber nur, wenn es etwas gibt, was mit der Ausnahme gemacht werden kann).

    
Martin York 29.12.2010 08:18
quelle
4

Tatsächlich empfehlen Sutter und Alexandrescu dieses Muster in ihren "C ++ Coding Standards" / p>     

Antonio Pérez 29.12.2010 08:24
quelle

Tags und Links