RAII und Exceptions
-
Wie wir alle wissen, sind Exceptions im Destruktor böse.
Aber wie verträgt sich das mit RAII? Die Idee hinter RAII ist doch, das Scoping auszunutzen, um automatisch Ressourcen freizugeben. Das macht es zu einer besseren Lösung als die finally-Klauseln in anderen Sprachen.
Aber, was ist, wenn beim Freigeben eine Exception passiert? Bei komplexeren Freigabeprozeduren dürfte das schnell eine Möglichkeit sein. Also, was tun?
-
Das werfen einer Exception im Destruktor ist nicht böse, sondern nur dass sie hinausgelangt.

Ein wenig spitzfindig, aber das bringt dich auf die Lösung des Problems. Fang und behandle einfach die möglichen Exceptions im Destruktor selbst.
-
drakon schrieb:
Fang und behandle einfach die möglichen Exceptions im Destruktor selbst.
Hattest du schon mal den Fall, dass das im DTor passieren könnte?
Ich entsinn mich gerade nicht daran, so was schon mal gehabt zu haben...
Und was macht man, wenn man die Exception dort nicht behandeln kann? Doch weiterwerfen? Einfach "verschlucken"?bb
-
drakon schrieb:
Fang und behandle einfach die möglichen Exceptions im Destruktor selbst.
Ganz so einfach ist es dann doch nicht.

Oft möchte die Klasse nach aussen mitteilen, dass was schiefgelaufen ist. Zum Beispiel, wenn sie den Fehler nicht selbst beheben kann.In der Tat ist die Angelegenheit nicht ganz trivial. Neben der sprachlichen Implementierung muss man sich grundsätzlich überlegen, wie auf solche Fehler im semantischen Sinne reagiert wird (was soll mein Programm tun, wenn es diese Ressource nicht freigeben kann). Teilweise gibt es darauf wirklich keine befriedigende Antwort.
-
Hehe. Ich wollte das einfach noch in Klammern setzen, aber habe es dann gelassen..;)
Klar ist es nicht so trivial, allerdings darf man einfach keine Exceptions aus dem dtor werfen. Da soll man auch nichts nach aussen weiterleiten. Man muss halt im Notfall, falls es wirklich eine Exception beim freigeben geben kann (was mir natürlich auch noch nie untergekommen ist) die wohl oder übel schlucken, wenn man sie nicht behandeln kann. In diesem Falle ist imo schlucken besser, als doch etwas zu werfen. So kann sich der Fehler nicht auf das ganze System auswirken, sondern hinterlässt nur etwas unbereinigt.
Aber eben. Wenn man z.B Speicher nicht mehr freigeben kann, dann ist es eh schon gelaufen.

-
dv_ schrieb:
Aber, was ist, wenn beim Freigeben eine Exception passiert? Bei komplexeren Freigabeprozeduren dürfte das schnell eine Möglichkeit sein. Also, was tun?
Naja. Gegenfrage: was tun, wenn - ganz ohne RAII und Exceptions - beim Freigeben irgendwas "danebengeht"? Hm? Was machst du da?
Und wenn deine Antwort jetzt nicht total seltsam ausfällt, dann kannst du dasselbe auch im Destruktor machen.
-
hustbaer schrieb:
was tun, wenn - ganz ohne RAII und Exceptions - beim Freigeben irgendwas "danebengeht"? Hm? Was machst du da?
weinen

Und wenn deine Antwort jetzt nicht total seltsam ausfällt, dann kannst du dasselbe auch im Destruktor machen.
Dingsa::~Dingsda() { try { deallocate_some_memory(); free_some_resources(); close_any_open_ports(); } catch (...) { cry(VERY_LOUD); } }*scnr*
-
unskilled schrieb:
drakon schrieb:
Fang und behandle einfach die möglichen Exceptions im Destruktor selbst.
Hattest du schon mal den Fall, dass das im DTor passieren könnte?
Ich entsinn mich gerade nicht daran, so was schon mal gehabt zu haben...
Und was macht man, wenn man die Exception dort nicht behandeln kann? Doch weiterwerfen? Einfach "verschlucken"?bb
Es muss ja nicht unbedingt eine C++ Exception sein. Nehmen wir an, wir haben eine C-API für die Soundkarte. Die API-Aufrufe haben die C-typischen Rückgabewerte um auf Fehler zu Prüfen. Die schreibst einen Wrapper. Im Ctor allozierst Du die Ressourcen, im Dtor gibst Du sie wieder frei. Beim Aufruf der Freigabe-Funktion wird ein Fehlercode zurückgegeben. Du hast das gleiche Problem wie bei try-catch und Exceptions im Dtor. Und ja, so was kommt durchaus mal vor.
-
dv_ schrieb:
Aber, was ist, wenn beim Freigeben eine Exception passiert? Bei komplexeren Freigabeprozeduren dürfte das schnell eine Möglichkeit sein. Also, was tun?
.. einfach auch eine Exception werfen, aber nur, wenn std::uncaught_exception() false liefert (C++-Standard Kapitel 15.5.3 [except.uncaught]).
-
Dazu noch ein Link (Herb Sutter):
http://www.gotw.ca/gotw/047.htmSimon
-
Tachyon schrieb:
Du hast das gleiche Problem wie bei try-catch und Exceptions im Dtor. Und ja, so was kommt durchaus mal vor.
Da hilft im Grunde nur einen Logging Mechanismus zu implementieren, bei dem der Destruktor sein Problem hinterlegen kann. Im Destruktor eine Exception zu werfen heißt konkret, daß jeder Destruktor dies prinzipiell darf was zu unkontrollierbaren Programmen führt, da sie mit unexpected() aussteigen. Im Grunde kann man alle destruktoren als Foo::~Foo() throw() definieren, da nur dieses Verhalten zu handhaben ist.
-
Werner_logoff schrieb:
dv_ schrieb:
Aber, was ist, wenn beim Freigeben eine Exception passiert? Bei komplexeren Freigabeprozeduren dürfte das schnell eine Möglichkeit sein. Also, was tun?
.. einfach auch eine Exception werfen, aber nur, wenn std::uncaught_exception() false liefert (C++-Standard Kapitel 15.5.3 [except.uncaught]).
std::uncaught_exception() halte ich für eine der sinnlosesten Funktionen von überhaupt, da sie nicht aussagekräftig ist.
(EDIT: bitte unbedingt den hier geposteten gotw Artikel lesen, der erklärt sehr schön wieso)Davon abgesehen halte ich es für eine ziemlich fragwürdige Praktik, das Verhalten eines Destruktors davon abhängig zu machen, ob es "safe" ist, eine Exception zu werfen. Bzw. halte ich es überhaupt für fragwürdig, einen Dtor Exceptions werfen zu lassen.
Es gibt allerdings drei IMO sinnvolle Möglichkeiten zu reagieren:
- die Exception ganz schlucken ohne irgendwas zu machen
- das Programm abbrechen
- den Fehler einem "Manager-Objekt" mitteilen
Welche von diesen Möglichkeiten "ideal" ist, kommt natürlich auf das Programm am das man schreibt, bzw. genauer den Programmteil.
Mit Möglichkeit (3) meine ich z.B.: Angenommen man hat ein Temp-File, das man aus irgendeinem Grund nicht löschen kann, und es ist irre wichtig dass das File doch irgendwann gelöscht wird. Dann kann man diesen Fehler einer Klasse mitteilen, die sich den Filenamen merkt, und dann vielleicht alle paar Sekunden versucht die "übriggebliebenen" Files zu löschen. Wenn es wichtig ist kann man die Filenamen auch irgendwo abspeichern (Datenbank), damit die Datei, falls das Programm beendet wird bevor die Datei nun endlich gelöscht werden konnte, bei einem späteren Programmstart gelöscht werden kann. Etwas in der Art.
Etwas anderes ist IMO mit oder ohne Exceptions nicht wirklich sinnvoll. Oder will man Cleanup-Code abbrechen/unterbrechen, weil ein Teil des Cleanup nicht funktioniert hat? Dann wird ja der weitere Cleanup-Code der darauf folgt erst wieder übergangen.
Falls es andere sinnvolle Möglichkeiten gibt, wäre ich daran interessiert, zu erfahren welche.
p.S.: Fehler loggen ist natürlich auch gut. Würde ich zu Variante (3) rechnen.
-
Exceptions in c++ sind zu kompliziert
-
avrül schrieb:
Exceptions in c++ sind zu kompliziertwas hättest du denn gern einfacher?
-
wurde "zensiert"
-
avrül schrieb:
wurde "zensiert"
So wie ich die Moderatoren hier kenne, löschen die nicht wahllos Beiträge. Eine sachliche Begründung wäre wahrscheinlich kein Problem gewesen. Ich kann mir aber vorstellen, dass es sich um typisches C++-Bashing-Getrolle gehandelt hat, und in diesem Fall kann ich die Löschung unterstützen, sowas brauchen wir hier echt nicht.
-
hustbaer schrieb:
avrül schrieb:
Exceptions in c++ sind zu kompliziertwas hättest du denn gern einfacher?
Auf eine Art finde ich Exceptions gut, anderseits wurde imo nicht gut genug abgesteckt, wo sie gebraucht werden sollen. Da scheinen auch die Meinungen immer mal wieder recht auseinander zu gehen. Von dem her finde ich auch, dass Exceptions besser definiert hätten werden können.
Nichts desto trotz empfinde ich sie als einen Segen im Gegensatz zu Fehler Rückgabewerte.

-
drakon schrieb:
...anderseits wurde imo nicht gut genug abgesteckt, wo sie gebraucht werden sollen. ...
Nunja ... aber entspricht das nicht einfach dem gesamten Profil von C++?
Eigentlich schreibt der Standard nie vor, wo man was verwenden sollte, sondern lediglich, wo man etwas nicht verwenden kann.
Der Standard schreibt auch nicht vor, dass Destruktoren keine Exceptions durchlassen dürften, angeforderter dynamischer Speicher freigegeben werden muss, Variablennamen aussagekräftig sein müssen, ....Schon bei const gibt's mehr als eine Meinung, was "best practice" ist und bei Exceptions ist das halt nicht anders.
Finde ich eigentlich besser als wenn "wohlüberlegte Ausnahmen" nicht mehr möglich wären.Gruß,
Simon2.
-
Simon2 schrieb:
drakon schrieb:
...anderseits wurde imo nicht gut genug abgesteckt, wo sie gebraucht werden sollen. ...
Nunja ... aber entspricht das nicht einfach dem gesamten Profil von C++?
Eigentlich schreibt der Standard nie vor, wo man was verwenden sollte, sondern lediglich, wo man etwas nicht verwenden kann.
Der Standard schreibt auch nicht vor, dass Destruktoren keine Exceptions durchlassen dürften, angeforderter dynamischer Speicher freigegeben werden muss, Variablennamen aussagekräftig sein müssen, ....Ich meinte nicht im Standard, sondern eher Allgemein. Das Thema Exceptions als allgemeines Konzept ist nicht so etabliert, wie andere Konstrukte in C++. Vor allem unterscheiden sie sich von innerhalb der Sprachen von Einsatzgebieten. Das ist imo das Problem. Z.B die Technik der Polymorhpie gibt es ja in sehr vielen Sprachen und da ist aber ziemlich genau bestimmt, wann und wo man es am ehesten einsetzt. Klar gibt es je nach dem Unterschiede, aber ein C# Programmierer ist nicht verwirrt, wenn er an gewissen Orten in C++ Code Polymorphie sieht. Bei Exceptions ist da allerdings anderst. Respektive der C# wird soch sehr oft in C++ Code fragen, warum da keine Exception geworfen wird, weil das in C# sonst so üblich wäre.
Um kurz auf deine Beispiele einzugehen. OK, das mit den Exceptions und Destruktor lasse ich mal weg, aber das mit dem dynamischen Speicher ist doch in jeder Sprache so, oder? - Wenn man Speicher anfordert, dann muss man ihn wieder freigeben, ob das jetzt ein GC macht, oder man selbst ist vom Prinzip her ja egal. Variablennamen sollten aussagekräftig und verständlich sein. Das kann man auch praktisch in jeder Sprache so allgemein sagen.
Aber wann eine Exception geworfen werden sollte kann imo nicht pauschal gesagt werden.
-
drakon schrieb:
...Ich meinte nicht im Standard, sondern eher Allgemein. ...
Ach so.
Hatte ich mißverstanden.drakon schrieb:
...Aber wann eine Exception geworfen werden sollte kann imo nicht pauschal gesagt werden.
Das erlebe ich auch so: Es gibt keinen sprachübergreifenden Konsens.
Nichteinmal innerhalb der Sprache (kenne jetzt aber nur C++ & Java) scheint es so 100%ig klar zu sein, wann man welche Exception wirft (und wann man mit Returncodes oder anderen Techniken arbeitet).
Das ist IMHO aber nicht direkt ein Phänomen mit Exceptions, sondern spiegelt eher die unterschiedlichen Herangehensweisen an "Fehler-/Ausnahme-/...-Situationen" wider. In C gab's das Problem früher auch schon mit "Returncode vs. errno".Eins gibt's allerdings schon: "Exception fängt man nur da, wo man sie auch bearbeiten kann"...
Gruß,
Simon2.