Pointer auf null testen hier sinnvoll?
-
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, ...
-
designfehler schrieb:
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.
Das fällt dann in die 0.00001%.

-
cooky451 schrieb:
Und dann wirf gleich noch mal einen Blick auf std::unique_ptr. Ein delete außerhalb der Standardbibliothek ist (zu 99.9999%?) ein Designfehler.
Jetzt geht das wieder los ... Fanatiker.
-
Wo er Recht hat, hat er Recht.

-
Im Umkehrschluss bedeutet das, alle Programme ohne Smartpointer haben einen Designfehler? Was ist mit jenen aus dem Jahr 2005 oder frueher? Ist C zu verwenden an sich auch ein Designfehler? Woher kommt der Wert 99.99...%, warum nicht 80%?
-
Den Umkehrschluss kannst Du nicht machen. Vielleicht braucht irgendein Programm ja auch überhaupt keine Zeiger, somit auch kein delete und somit auch keine Smartpointer.

Ich benutze die auch nicht ständig, auch unique_ptr nicht. Aber mein aktuelles Projekt lehrt mich, dass die Vorteile zu groß sind. Zu leicht vergisst man Mal ein delete oder macht aus Versehen eine unerwünschte Kopie (bzw. vergisst das Privatisieren von copy-ctor & operator=). Da kann mir jetzt jeder sagen "Dann programmierst Du halt schlecht.". Umso größer das Projekt wird, umso wahrscheinlicher ist es jedoch, dass es in einem Promille der Fälle passiert und oft sind damit verbundene Fehler schwer zu finden (oder niemand merkt gar, dass er ein delete vergaß und erzeugt Speicherlecks).
-
Eisflamme schrieb:
Den Umkehrschluss kannst Du nicht machen. Vielleicht braucht irgendein Programm ja auch überhaupt keine Zeiger
Ähemm.. bitte richtig formulieren

-
Bitte sagen, was falsch formuliert ist.
Und hast Du ein Verständnisproblem oder ist die Aussage nicht vielmehr komplett klar, sodass es keinen Grund dazu gibt darauf rumzuhacken?