nach delete pointer auf nullptr setzen



  • Wenn ich code von anderen leuten sehe fällt mir auf, dass einige um ein objekt zu löschen immer folgendes schreiben

    delete obj;
    obj = nullptr;
    

    und andere die zweite zeile einfach weglassen.

    Was ist die bessere variante? mit dieser zeile ist es zwar theoretisch sicherer, aber die frage ist ob sich das auf dauer in komplexen algorithmen lohnt, weil eigendlich nur rechenzeit "verschwendet" wird.

    Wie ist eure Meinung zu diesem thema?


  • Mod

    Was wäre da dran denn sicherer?

    Entweder braucht es das Nullsetzen (zum Beispiel Blätter von Baumstrukturen) oder es ist bloß Cargo-Cult von Leuten, die das mal gesehen, aber nicht verstanden haben.



  • Na ja, erstmal ist meine Meinung, dass man delete gar nicht aufrufen brauchen sollte.

    Tut man es dennoch, so kommt es drauf an. Geschieht das im dtor einer Klasse, ist das einfach unnötig. Möchte man den klaren Zustand der Nicht-Existenz darstellen, ist das durch nullptr natürlich schlau, wenn die Lebensdauer des Pointers kürzer ist als die des beinhaltenden Objekts. Wiederum gehört Lebenszeitmanagement in C++ aber durch RAII und somit Smartpointer abgedeckt, sodass mir auch das wehtäte. (Edit: eigene Container und Trees Mal außen vor gelassen, das ist ja aber eher Spezial- als Regelfall).

    Im ctor einer Klasse setze ich alle Zeiger auf nullptr, wenn sie nicht dort sondern später gesetzt werden. Das sind dann aber i.d.R. Zeiger als Referenzen, keine, deren referenziertes Objekt innerhalb dieser Klasse mit new erstellt wird.



  • Das Nullsetzen hat den Vorteil, dass es beim anschließenden Zugriff immer sofort Crashed. Das macht die Fehlersuche oft erheblich einfacher.



  • TNA schrieb:

    Das Nullsetzen hat den Vorteil, dass es beim anschließenden Zugriff immer sofort Crashed. Das macht die Fehlersuche oft erheblich einfacher.

    Genau nicht. 1. muss es nicht zwingend crashen beim Zugriff auf einen Null-Pointer und 2. hilft einem der Debugger den Zugriff auf schon gelöschte Objekte zu erkennen. Ausserdem werden double deletes durch nullen des Pointers verschleiert.



  • theta schrieb:

    TNA schrieb:

    Das Nullsetzen hat den Vorteil, dass es beim anschließenden Zugriff immer sofort Crashed. Das macht die Fehlersuche oft erheblich einfacher.

    Genau nicht. 1. muss es nicht zwingend crashen beim Zugriff auf einen Null-Pointer und 2. hilft einem der Debugger den Zugriff auf schon gelöschte Objekte zu erkennen. Ausserdem werden double deletes durch nullen des Pointers verschleiert.

    zu 1.
    Die Dereferenzierung eines Null-Pointer führt meines Wissens nach immer zum Absturz.

    zu 2. Dazu muss der Fehler reproduzierbar sein, man muss eine Debugversion der Programms verwenden können und das Programm Debuggen können. Das ist z.b. im Embeddedbereicht oft nicht möglich.

    Warum sind double deletes per se ein Problem wenn der Zeiger genullt wird?



  • also zu 1)
    Wenn ich im Release-Mode einen Klassenpointer habe und der nullptr ist und dann eine Methode aufrufe, dann führt das nicht sofort zum Absturz sondern erst bei einer gewissen Verschachtelungstiefe und auch nur bei Attributzugriff. Nachvollziehbarkeit ist leider ziemlich eingeschränkt, wenn der komplette Stack nicht immer getraced ist.


  • Mod

    TNA schrieb:

    Warum sind double deletes per se ein Problem wenn der Zeiger genullt wird?

    Weil es ein Fehler in der Programmlogik ist, wenn so etwas unabsichtlich passiert. Den möchtest du also möglichst nicht vertuschen.



  • Eisflamme schrieb:

    also zu 1)
    Wenn ich im Release-Mode einen Klassenpointer habe und der nullptr ist und dann eine Methode aufrufe, dann führt das nicht sofort zum Absturz sondern erst bei einer gewissen Verschachtelungstiefe und auch nur bei Attributzugriff. Nachvollziehbarkeit ist leider ziemlich eingeschränkt, wenn der komplette Stack nicht immer getraced ist.

    Klar, solange der this-Zeiger nicht dereferenziert wird passiert natürlich nichts. Jede Methode die ein Objekt benötigt sollte aber im Normalfall direkt oder indirekt auf ein Attribut zugreifen. Bei virtuellen Funktionen sollte es sogar direkt beim Aufruf crashen.



  • TNA schrieb:

    Warum sind double deletes per se ein Problem wenn der Zeiger genullt wird?

    Weil es da gleichzeitig passieren kann, dass dies kein Einzelfall ist und das auch mit anderen Zeigern/Variablen passiert.



  • TNA schrieb:

    Warum sind double deletes per se ein Problem wenn der Zeiger genullt wird?

    Für mich sind double deletes auch ein Zeichen von der Mehrfachbenutzung von Variablen - etwas das selten sinnvoll ist.

    Zudem kamen bei meinen Code, auch zu der Zeit als ich RAII und Smartpointer noch nicht kannte, zu geschätzten 95% im Destruktor vor. Dort ist Nullen immer unsinnig, da kein Zugriff mehr erfolgt (Wenn doch hat man ganz andere Probleme).

    Ich habe Projekte miterlebt wo Variablen häufig verwendet, eher missbraucht, wurden. Und ja, das ist ein Fall wo das Nullen nötig ist; Dies ist in 99% durch Fehldesign geschuldet.



  • [quote="asc"]

    TNA schrieb:

    Ich habe Projekte miterlebt wo Variablen häufig verwendet, eher missbraucht, wurden. Und ja, das ist ein Fall wo das Nullen nötig ist; Dies ist in 99% durch Fehldesign geschuldet.

    Haha^^, ich kann mich an ein Buch zur DirectX9 und Spieleprogrammierung in 21 Tagen erinnern: Der Autor hat doch tatsächlich eine ganze Reihe globaler Variablen definiert, die einfach nur da waren, damit nicht immer neue Variablen erstellt werden müssen.

    Statt:

    for(float f = 0.0f; f < xyz; f += 0.1)
    {
    }
    

    nämlich

    for(g_tempF = 0.0; g_tempF < xyz; g_tempF += 0.1)
    {
    }
    

    Sowas in der Richtung, und das auch mit anderen Datentypen wie Vektoren oder sonstwas (ich glaube sogar Listen)


  • Mod

    Das bestätigt mal wieder voll sämtliche Vorurteile gegen Bücher mit solchen Titeln.

    (Ich muss zugeben, in meinem allerersten Programm habe ich das auch so gemacht. Meine Güte wurde das schneller, als ich den Unsinn später entfernt habe! Ist zwar lange her, aber eine solche Lektion vergisst man nicht. Was wohl darauf hindeutet, dass unser Spieleprogrammierautor wohl kaum jemals mehr als seine ersten Programme geschrieben hat, bevor er das Buch schrieb)



  • Begründet wurde das damit, dass es ja Laufzeit kostet eine Variable zu erstellen. Das stimmt ja auch irgendwo, aber das sind nicht die Bottlenecks und vor allem leidet die Übersichtlichkeit so stark darunter.

    (Ich kann ja schon kaum mit typenlosen Sprachen wie PHP oder Javascript O_o)



  • Sowas ist Bloedsinn. Variablen sind, soweit moeglich, in Registern. Und selbst wenn mal nicht alle Variablen in Register passen, alloziert der Compiler alle lokalen Variablen in einem Rutsch - da ist es voellig egal, wie viele.



  • Skym0sh0 schrieb:

    Haha^^, ich kann mich an ein Buch zur DirectX9 und Spieleprogrammierung in 21 Tagen erinnern: Der Autor hat doch tatsächlich eine ganze Reihe globaler Variablen definiert, die einfach nur da waren, damit nicht immer neue Variablen erstellt werden müssen.

    Versuch mal in solchen Projekten Fehler aufzuspüren. Eine Variable statische, öffentliche Klassenvariable wurde in einer DLL etwa für 5 verschiedene Zwecke missbraucht, die noch dazu Timergesteuert darauf zugegriffen hatten.


  • Mod

    Skym0sh0 schrieb:

    Begründet wurde das damit, dass es ja Laufzeit kostet eine Variable zu erstellen.

    Nein, tut es nicht. Außer das Objekt hat einen Konstruktor. Dann kannst du dir den mit dem Trick aber nicht sparen.

    Was der Trick aber aktiv verhindert sind allerlei Optimierungen. Schließlich muss hier ein globaler Programmzustand aktuell gehalten werden, sofern der Compiler nicht beweisen kann, dass dies später woanders keine Auswirkungen hat (was schwierig sein dürfte). Das heißt, der Wert muss mindestens schon einmal in den RAM zurück geschrieben werden, anstatt dass einfach gar nichts passiert.



  • SeppJ schrieb:

    Das heißt, der Wert muss mindestens schon einmal in den RAM zurück geschrieben werden, anstatt dass einfach gar nichts passiert.

    Ja, und wenn du Pech hast, liegt die globale Variable irgendwo in der Ferne und du hast einen Cache-Miss. Langsam wirds richtig teuer...

    Ganz lustig ist sowas aber erst in Multithreaded-Umgebungen.

    😮


Log in to reply