Pointer auf null testen hier sinnvoll?
-
Jo, also wie schon gesagt wurde: Wenn es nicht mehr genug Speicher gibt, wirft
operator neweinenstd::bad_alloc, was auch völlig verständlich ist (denn wenn ein Objekt nicht erzeugt werden konnte, ist der weitere Programmablauf meistens sinnlos).Ich kann mir aber leider nicht im Geringsten vorstellen, wo das allokieren mit der
nothrowÜberladung sinnvoll sein könnte.
-
XSpille schrieb:
Also ich habe in meinem Destruktor schon gelegentlich ein delete drin stehen...
In dem Fall habe ich mit std::unique_ptr keinen Mehrwert, da es ja durch das delete im Destruktor eh exceptionsafe ist.Eieiei, bist du dir da sicher?
class Blub { private: int* stuff; public: Blub() : stuff(new int(42)) { functionThatMightThrow(); // wenn das hier eine exception wirft, leakest du } ~Blub() { delete stuff; } };Mit std::unique_ptr wär das nicht passiert...

-
XSpille schrieb:
Also es gibt schon Fälle, wo ich delete verwende. Mir kommt es jetzt nicht auf die 4 Nachkommastellen an

Sollte es aber.
XSpille schrieb:
Verwendest du als Member-Variablen immer std::unique_ptr (oder std::shared_ptr etc.) statt Zeigern?
Natürlich, wenn sie besitzend sind.
XSpille schrieb:
Also ich habe in meinem Destruktor schon gelegentlich ein delete drin stehen...
Fehler.
XSpille schrieb:
In dem Fall habe ich mit std::unique_ptr keinen Mehrwert, da es ja durch das delete im Destruktor eh exceptionsafe ist. (bei shared_ptr hat man natürlich einen Mehrwert)
Denkste. Mal sowas gemacht?
class foo { int* p_; int* q_; public: foo() : p_(new int) , q_(new int) {} ~foo() { delete p_; delete q_; } };Was meinst du, passiert, wenn das zweite new int eine Exception wirft? Solche Fälle kommen durchaus vor. Du weißt nicht wo Exceptions geworfen werden können. Nachher rufst du im Konstruktor sonst noch irgendwelche Funktionen auf. Gleiches Problem. Nein, immer Smartpointer verwenden. Ich mache das sogar bei Handles (WinAPI/OpenGL C Kram halt) mit einem custom deleter*. Wenig Aufwand, und ich muss mir keine Gedanken mehr über Exception Safety machen, weil das einfach by Design garantiert ist. Und nebenbei macht es die move Konstruktoren auch leichter und einheitlich.(make_unique kommt ja hoffentlich bald in den Standard, dann kann man das new auch endlich ins Altersheim schicken.)
* Ganz genau genommen hab' ich da eine eigene Klasse unique_handle bei der man einen Wert für invalid angeben kann.

Edit: Jetzt ist dot mir zuvor gekommen.
-
@dot,
Nein, dort entsteht kein Leak. Der Destruktor wird aufgerufen. Die Exception muss in der Initialisierungsliste fliegen, damit das Leak passiert. Cooky hat das schon aufgezeigt.Aber ich stimme der Aussage von Cooky trotzdem nicht zu. Das ist völlig übertrieben ausgedrückt.
Grüssli
-
Dravere schrieb:
@dot,
Nein, dort entsteht kein Leak. Der Destruktor wird aufgerufen. Die Exception muss in der Initialisierungsliste fliegen, damit das Leak passiert. Cooky hat das schon aufgezeigt.Dravere schrieb:
Aber ich stimme der Aussage von Cooky trotzdem nicht zu. Das ist völlig übertrieben ausgedrückt.
Dann will ich zumindest ein Beispiel mit Begründung, warum es da sinnvoller ist keinen Smartpointer zu verwenden.

-
Dravere schrieb:
@dot,
Nein, dort entsteht kein Leak. Der Destruktor wird aufgerufen.Nein. http://herbsutter.com/2008/07/25/constructor-exceptions-in-c-c-and-java/
Edit: http://ideone.com/N2p34
Edit 2: Du bist ja fies, cooky. Editierst einfach eine Antwort rein, die meine überflüssig macht

-
Michael E. schrieb:
Edit 2: Du bist ja fies, cooky. Editierst einfach eine Antwort rein, die meine überflüssig macht

Ja.. ideone hat mal wieder ewig zum Laden gebraucht, da war der zweite Teil der Antwort einfach schneller geschrieben. Nennt sich lazy evaluation, super Sache!

-
cooky451 schrieb:
...
Ich hätte meine Hand ins Feuer gelegt. Habe es gerade mit 3 anderen Kompilern probiert. Womit zum Teufel verwechsle ich das gerade?
Eindeutig, Zeit für mich ins Bett zu gehen. Die letzten Tage waren für mein Hirn doch zu viel, inzwischen ist es durchgeschmorrt

cooky451 schrieb:
Dann will ich zumindest ein Beispiel mit Begründung, warum es da sinnvoller ist keinen Smartpointer zu verwenden.

Ich fürchte, dass das keine gute Idee ist, wenn ich gerade realisiere, dass meine Gedanken nicht mehr ganz korrekt laufen

Grüssli
-
Dravere schrieb:
@dot,
Nein, dort entsteht kein Leak. Der Destruktor wird aufgerufen. Die Exception muss in der Initialisierungsliste fliegen, damit das Leak passiert. Cooky hat das schon aufgezeigt.Standaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaard...
N3337 §15.2 Klausel 2 schrieb:
An object of any storage duration whose initialization [...] is terminated by an exception will
have destructors executed for all of its fully constructed subobjects [...]
-
edit: hat sich mit sones post erledigt
ich verstehe nicht ganz, wieso das mit smartpointern funktioniert.
#include <iostream> using namespace std; struct Foo { unique_ptr<int> p; Foo() : p(new int) { throw 42; } ~Foo() { cout << "Foo destroyed\n"; } }; int main() { try { Foo foo; } catch(...) { cout << "catch\n"; } }p wird ja mit foo zerstört, aber wann ist das? Nach dem Ende des Programms?
catch
-
Sone schrieb:
Standaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaard...
N3337 §15.2 Klausel 2 schrieb:
An object of any storage duration whose initialization [...] is terminated by an exception will
have destructors executed for all of its fully constructed subobjects [...]Joa, da steht – im extra fett hervorgehobenen Teil – ganz deutlich ein ganz bestimmtes Wort: subobjects
Das heißt*, dass die Variante mit den Smartpointern funktioniert – da steht nichts vom Aufruf des Destruktors des Objekts, das gerade konstruiert wurde, als die Ausnahme ausgelöst wurde.
*(außer der Standard hat irgendeine merkwürdige eigene Definition dieses Wortes)
-
cooky451 schrieb:
Was meinst du, passiert, wenn das zweite new int eine Exception wirft? Solche Fälle kommen durchaus vor.
Das hast nichts mehr mit der Realität zu tun. Inwiefern interessiert denn noch Exception-Sicherheit, wenn das Allokieren von ein paar Byte fehl schlägt? Was genau will man in so einer Extremsituation noch retten?
-
µ schrieb:
Das hast nichts mehr mit der Realität zu tun. Inwiefern interessiert denn noch Exception-Sicherheit, wenn das Allokieren von ein paar Byte fehl schlägt? Was genau will man in so einer Extremsituation noch retten?
Andererseits können Konstruktoren sehr häufig Exceptions werfen, wenn man sich an RAII hält.
-
µ schrieb:
cooky451 schrieb:
Was meinst du, passiert, wenn das zweite new int eine Exception wirft? Solche Fälle kommen durchaus vor.
Das hast nichts mehr mit der Realität zu tun. Inwiefern interessiert denn noch Exception-Sicherheit, wenn das Allokieren von ein paar Byte fehl schlägt? Was genau will man in so einer Extremsituation noch retten?
z.b. daten retten/speichern, die sich im laufe des programms angesammelt haben, bevor es zur out of memory situation kam.
-
@Hyde++
p ist zum Zeitpunkt an dem die Exception geworfen wird vollständig konstruiert, also wird auch der Destruktor aufgerufen. Wie kommst du auf das Ende des Programms? Das passiert quasi sofort.µ schrieb:
Das hast nichts mehr mit der Realität zu tun. Inwiefern interessiert denn noch Exception-Sicherheit, wenn das Allokieren von ein paar Byte fehl schlägt? Was genau will man in so einer Extremsituation noch retten?
Es geht hier um sauberes Programmieren, da interessiert mich die Realität doch nicht.

Aber mal ernsthaft: Das Objekt kann auch etwas anderes als ein int sein. Was ist, wenn da eine Datenbankverbindung nicht mehr geschlossen wird oder Ähnliches? (Was genau die Exception wirft ist für das Argument doch völlig unerheblich.) Und warum sollte ich auf 100%ige Sicherheit verzichten, wenn sie nichts kostet? C++ macht es einem so einfach, saubere und sichere Programme zu erstellen. Viel einfacher als jede andere mir bekannte Sprache; man muss sich einfach nur mal an ein paar kleine Regeln halten. Ich verstehe nicht was man dagegen haben könnte.
(Und ich verstehe nicht, was daran so schwer ist, dass einige mehr oder weniger erfahrende Leute hier sich immer noch nicht daran halten.)
-
cooky451 schrieb:
XSpille schrieb:
Also ich habe in meinem Destruktor schon gelegentlich ein delete drin stehen...
Fehler.
Weswegen Fehler?

cooky451 schrieb:
XSpille schrieb:
In dem Fall habe ich mit std::unique_ptr keinen Mehrwert, da es ja durch das delete im Destruktor eh exceptionsafe ist. (bei shared_ptr hat man natürlich einen Mehrwert)
Denkste. Mal sowas gemacht?
class foo { int* p_; int* q_; public: foo() : p_(new int) , q_(new int) {} ~foo() { delete p_; delete q_; } };Ich bin mir bewusst, dass es problematisch wird, wenn im zweiten new eine Exception fliegt. Allerdings stelle ich mal die Frage, warum du die Variablen dynamisch allokierst? Wann brauch man schonmal zwei Allokationen in einem Konstuktor? Ich dachte an setter, die den Besitz eines Objektes übernehmen. Da kann dann keine Exception fliegen und ein delete im Destruktor ist völlig exceptionsicher...
struct foo{ foo() : bar_ptr(nullptr){} ~foo() { delete bar_ptr; } void set_bar(std::unique_ptr<bar>&& bar){ bar_ptr = bar.release(); } // unique_ptr als Besitz-Kennzeichnung bar* bar_ptr; };
-
XSpille schrieb:
Weswegen Fehler?

Weil schlecht.
XSpille schrieb:
Ich bin mir bewusst, dass es problematisch wird, wenn im zweiten new eine Exception fliegt. Allerdings stelle ich mal die Frage, warum du die Variablen dynamisch allokierst?
Um hier eben ein Beispiel hinzufrickeln? Was soll die Frage überhaupt, ist doch wohl mehr als offensichtlich welche Fälle mit diesem Beispiel gezeigt werden?

XSpille schrieb:
Wann brauch man schonmal zwei Allokationen in einem Konstuktor?
Das betrifft jede Situation in der du 1. eine Ressource reservierst, die man wieder schließen muss und 2. danach irgendetwas machst, was eine Exception werfen könnte. Das kommt doch schon mal vor.
XSpille schrieb:
Ich dachte an setter, die den Besitz eines Objektes übernehmen. Da kann dann keine Exception fliegen und ein delete im Destruktor ist völlig exceptionsicher...
Dann ist das in diesem einen Fall halt exceptionsicher. Es gibt auch noch mehr Fälle, die ohne Smartpointer sicher sind. Hat aber mit dem Punkt nichts zu tun. So musst du dir darüber Gedanken machen, und wenn du das mal vergisst, kabumm. Wenn du einfach immer einen Smartpointer nimmst, kann das nicht passieren. Da das keinen Nachteil hat*, folgt logisch dass man immer Smartpointer nutzt.
* man muss eher weniger Code schreiben, und kann z.B. nicht vergessen Kopierkonstruktoren etc. private zu machen, bzw. man kann nicht aus versehen kopieren, weil das dann nicht kompiliert
Ich könnte mich hier schon wieder selbst zitieren, ich verstehe nicht was daran so schwer ist, sich an diese simple Regel zu halten, die so viele offensichtliche Vorteile und quasi keine Nachteile hat.
Dein Beispiel macht noch weiter deutlich wie schwachsinnig das Ganze wird. Der Code wäre viel simpler, wenn wenn bar_ptr ein Smartpointer wäre.
Edit: Und warum zum Geier holst du dir den unique_ptr über eine rvalue Referenz? Oo
-
XSpille schrieb:
Ich dachte an setter, die den Besitz eines Objektes übernehmen.
Benutzt du RAII? Wie löst du den Fall, dass du den Pointer bereits im Konstrukor initialisieren musst?
-
cooky451 schrieb:
Ein delete außerhalb der Standardbibliothek ist (zu 99.9999%?) ein Designfehler.
Als ich mal einen custom deleter für einen smart pointer geschrieben habe, war das delete glaub nötig.
-
µ schrieb:
cooky451 schrieb:
Was meinst du, passiert, wenn das zweite new int eine Exception wirft? Solche Fälle kommen durchaus vor.
Das hast nichts mehr mit der Realität zu tun. Inwiefern interessiert denn noch Exception-Sicherheit, wenn das Allokieren von ein paar Byte fehl schlägt? Was genau will man in so einer Extremsituation noch retten?
Das hat viel mit der Realität zu tun. Zumindest mit meiner.
Und zwar einfach deswegen, weil ich mich weigere mir bei jeder 2. Zeile (oder auch nur bei jeder 100. oder 100.) zu überlegen "könnte das mal relevant werden?". Weil das einfach zu viel Zeit frisst. Und vor allem über kurz oder lang mit an Sicherheit grenzender Wahrscheinlichkeit zu schlechtem Code führt.Wenn es um Exception-Safety geht, dann mach' ich das einfach.
Ob es 1 MB oder 1intoder 1charist ist mir dabei dann so richtig vollkommen egal.Ich habe mir dadurch auch angewöhnt überhaupt etwas anders zu programmieren. Dadurch bin ich dann auch an den Stellen schneller, wo man ganz sicher nicht auf Exception-Safety verzichten kann. Unterm Strich brauch' ich vermutlich trotzdem ein wenig länger, aber ich meine es zahlt sich aus: weniger Fehler, besser wartbarer Code, ...