Destructor-Handling bei Exception aus einem Constructor
-
hustbaer schrieb:
Ja, und deine Antwort ist immer noch falsch.
Weil "erst aufräumen, dann werfen" in einem Programm mit Exceptions nix verloren hat. Weil es zu Problemen (=Bugs) führt. Weil es falsch ist.Vielleicht liegt ein Missverständnis vor, was hier verschiedene Leute unter "falsch" verstehen. Für mich ist ein Programm "falsch" wenn es nicht das korrekte Ergebnis liefert (UB oder nicht korrekter Algorithmus) oder wenn es syntaktisch falsch ist.
Recht gebe ich dir dahingehend, dass es so "hochgradig nicht empfehlenswert" ist, oder "mit höherer Wahrscheinlichkeit zu Fehlern führt". Natürlich sollte man möglichst vermeiden in Konstruktoren Exceptions zu behandeln, aber manchmal hat man keine andere Wahl, und dann (und nur dann!) sollte man eben aufräumen und dann werfen.
Finnegan
-
Würde ich trotzdem so schreiben:
auto ptr = std::make_unique<std::shared_ptr<Type>>(other); // oder sogar std::move(other) ... // hier könnte immer Code eingefügt werden, der wirft c_api_with_callback(ptr.release());
Dein Code mag zwar in der Situation korrekt sein, beruht aber auf Subtilitäten (noexcept zwischen new und Callback-Aufruf) und geht deshalb sehr leicht kaputt. Deshalb lieber garantiert ausnahmensicher programmieren.
-
proof by contradiction schrieb:
Würde ich trotzdem so schreiben:
auto ptr = std::make_unique<std::shared_ptr<Type>>(other); // oder sogar std::move(other) ... // hier könnte immer Code eingefügt werden, der wirft c_api_with_callback(ptr.release());
Dein Code mag zwar in der Situation korrekt sein, beruht aber auf Subtilitäten (noexcept zwischen new und Callback-Aufruf) und geht deshalb sehr leicht kaputt. Deshalb lieber garantiert ausnahmensicher programmieren.
Wenn dort Code steht, der werfen kann ja, allerdings wird der Shared-PointerPointer ausschliesslich für diesen einen Aufruf der C-API erzeugt. Die beiden Zeilen oben würden im Code direkt untereinander stehen, und daher in diesem Fall eklatant das "keep it simple, stupid!"- und das "programmier' nicht für Code, der hier irgendwann vielleicht mal stehen könnte"-Prinzip verletzen.
Generell ist dein Hinweis jedoch sinnvoll, aber nur wenn dazwischen auch tatsächlich Code steht (Funktionsaufrufe) bei dem man sich nie ganz sicher sein kann
Finnegan
-
Finnegan schrieb:
Wenn dort Code steht, der werfen kann ja, allerdings wird der Shared-PointerPointer ausschliesslich für diesen einen Aufruf der C-API erzeugt. Die beiden Zeilen oben würden im Code direkt untereinander stehen, und daher in diesem Fall eklatant das "keep it simple, stupid!"- und das "programmier' nicht für Code, der hier irgendwann vielleicht mal stehen könnte"-Prinzip verletzen.
Letzteres ist aber ein deutlich wichtigeres Prinzip. Programming in the future sense. Du weißt nie, wie lange dein Code noch lebt. Irgendwann könnte ein anderer Programmierer da Code einfügen, der wirft, weil er schnell etwas fixen muss und/oder nicht auf Exceptionsafety achtet. Man muss so programmierern, das solche Fehler vermieden werden.
Darüber hinaus verletzt gezeigter Code jetzt auch nicht gerade KISS.
-
@Finnegan
Mir geht es dabei eher darum wie solcher Code überhaupt entsteht.Das erste was einem dabei auffallen sollte, wenn man sowas schreibt, ist, dass es hier ein Wartbarkeitsproblem gibt.
Und zwar dass man bei Code dieser Art immer aufpassen muss dass eine Änderung nicht zu einem Problem führt.
Und wenn man das mal verstanden hat, dann sollte man sich die Frage stellen: wie kann man das verhindern?
Die Antwort wurde ja schon gegeben.Und wenn man sich diese Dinge mal überlegt hat, dann sollte man mMn. solchen Code auch nicht mehr schreiben. Weil man schliesslich bereits weiss dass es problematisch ist, und wie man es besser macht.
Konstrukte dieser Art können also durchaus "richtig" sein, in dem Sinn dass ein Programm, so wie es ist, ohne weitere Änderungen, in keinem Fall UB sein wird, und in keinem Fall andere unerwünschte Dinge tun wird (z.B. Leaks, generell nicht das machen was das Programm machen sollte).
Sie deuten mMn. aber immer darauf hin dass der Programmierer etwas nicht verstanden hat oder es bewusst ignoriert.
Wenn du willst kannst du also sagen: der Code ist strenggenommen korrekt, aber der Programmierer ist defekt.@proof by contradiction
proof by contradiction schrieb:
Würde ich trotzdem so schreiben:
auto ptr = std::make_unique<std::shared_ptr<Type>>(other); // oder sogar std::move(other) ... // hier könnte immer Code eingefügt werden, der wirft c_api_with_callback(ptr.release());
Dein Code mag zwar in der Situation korrekt sein, beruht aber auf Subtilitäten (noexcept zwischen new und Callback-Aufruf) und geht deshalb sehr leicht kaputt. Deshalb lieber garantiert ausnahmensicher programmieren.
Wenn schon, dann so:
auto ptr = std::make_unique<std::shared_ptr<Type>>(other); ... // hier könnte immer Code eingefügt werden, der wirft c_api_with_callback(ptr.get()); // ... und jetzt darf auch c_api_with_callback werfen wenn es mag ptr.release();
Sonst gibt es nämlich ein Problem wenn die "C API" nicht wirklich eine "C API" ist, sondern C++ Code der hinter einer C-artigen Schnittstelle versteckt wurde.
-
Nathan schrieb:
Letzteres ist aber ein deutlich wichtigeres Prinzip. Programming in the future sense. Du weißt nie, wie lange dein Code noch lebt. Irgendwann könnte ein anderer Programmierer da Code einfügen, der wirft, weil er schnell etwas fixen muss und/oder nicht auf Exceptionsafety achtet. Man muss so programmierern, das solche Fehler vermieden werden.
Ja, das stimmt auch. Dieser kurze Code ist auch eigentlich ein schlechtes Beispiel dafür, was ich hier eigentlich vermeiden möchte: Dass Code Stück für Stück komplizierter wird und damit schwerer zu lesen und zu warten wird. Wenn man alle zukünftigen Eventualitäten berücksichtigt, kann sowas durchaus auch passieren. Schwer zu beurteilen, wo man da die Grenze zieht, und darüber kann man sicher lange diskutieren. Einen
unique_ptr
zu erstellen, der direkt in der nächsten Zeile seine Besitzansprüche aufgibt kommt mir etwas übertrieben vor - ich bevorzuge da etwas weniger Rauschen im Code, auch wenn ich ebenfalls Verfechter von Code bin, der hilft, zukünftige Fehler zu vermeiden (wie gesagt, ist alles etwas zweischneidig).Nathan schrieb:
Darüber hinaus verletzt gezeigter Code jetzt auch nicht gerade KISS.
Wie gesagt, nach meinem Bauchgefühl schon, weil es ein kleines bisschen unnötige Komplexität reinbringt durch den zusätzlichen
unique_ptr
. Vielleicht wäre es besser dasnew
in den Funktionsaufruf zu packen:c_api_with_callback(new std::shared_ptr<Type>(other));
... und so deutlich zu machen "es ist nicht geplant, das hier etwas dazwischen steht", und "
c_api_with_callback
trägt jetzt die Verantwortung für den Speicher".Finnegan
-
hustbaer schrieb:
Und wenn man sich diese Dinge mal überlegt hat, dann sollte man mMn. solchen Code auch nicht mehr schreiben. Weil man schliesslich bereits weiss dass es problematisch ist, und wie man es besser macht.
Wie bereits gesagt, da stimme ich generell zu! Ich habe lediglich ein Problem mit "immer" und "nie" - lieber "fast immer" und "fast nie", da es fast immer Ausnahmefälle gibt, und sei es nur für die handvoll Leute, welche die C++-Standardbibliothek programmieren, damit man überhaupt erstmal das Handwerkszeug hat, um auf solche Konstrukte verzichten zu können
Finnegan
-
hustbaer schrieb:
Ja, und deine Antwort ist immer noch falsch.
Weil "erst aufräumen, dann werfen" in einem Programm mit Exceptions nix verloren hat. Weil es zu Problemen (=Bugs) führt. Weil es falsch ist.es ist also falsch, weil es falsch ist. Ja, das klingt überzeugend
-
@Finnegan
Da hast du schon recht.
Blöd ist bei "fast immer" bzw. "fast nie" nur, dass es viele Idioten gibt die es einfach überlesen, weil sie meinen eh alles besser zu wissen.@großbuchstaben
Du musst den Rest meiner Beiträge lesen, da stehen genug Begründungen.
-
ich muß gar nichts
-
Natürlich nicht.
Ich muss aber auch nix, also z.B. dich nicht ernst nehmen.
-
@hustbaer: Können wir uns darauf einigen dass man auf Menschen, die man nicht ernst nehmen möchte, nicht antworten muss?
-
@Arcoth
Grundsätzlich ja.
In diesem speziellen Fall ... nein.Ich hoffe du kannst das nachvollziehen. Wäre etwas mühsam das erklären zu müssen.
-
doch, erklär mal genau - heiter' mich auf, nach diesem arbeitsreichen Tag
-
Achje...
Wenn du es nicht für nötig befindest meine Beiträge vollständig zu lesen, wieso sollte ich deine Einwände auf diese Beiträge dann ernst nehmen?Klar musst du nicht lesen was ich schreibe. Nur wenn du es nicht tust, dann sehe ich auch keinen grossen Sinn darin deine an mich gerichteten Beiträge ernst zu nehmen.
Sollte doch irgendwie nachvollziehbar sein, oder?
-
hustbaer schrieb:
Du musst den Rest meiner Beiträge lesen, da stehen genug Begründungen.
hustbaer schrieb:
Klar musst du nicht lesen was ich schreibe.
-
Dir das implizierte "wenn du mich kritisieren willst" dazuzudenken war anscheinend zu viel verlangt. Naja, wundert mich nicht.