if(pointer) delete pointer ?



  • Hallo,

    ich habe ein frage was pointer und delete angeht: Kann ich denn nicht anhand des pointerwertes überprüfen ob ich den pointer löschen soll oder nicht?:
    Folgender Code:

    struct STR {
       int* arr;
    };
    
    STR* fkt1()
    {
    STR* ret = new STR;
    
    ret->arr = new int[];
    
    return ret
    }
    
    free_qr1(STR* a)
    {
        delete [] a->arr;
        delete a;
    }
    
    irgendwo()
    {
         STR *qr1 = NULL;
    
         if(/*eine bedingung*/)
             qr1 = fkt1();
         else
            //setze qr1 nicht
    
         if(qr1) free_qr1();
    }
    


  • Klar kannst du und tust du doch auch..? Was funktioniert denn da nicht so wie's soll? Oder willst du den Code für "setze qr1 nicht" wissen? Lass den Pointer weiter auf 0 zeigen, damit ist er ja ungültig.



  • nicht unbeding, wenn du denn zeiger bei der deklaration nicht auf NULL initialisierst, kannst du nich prüfen ob er gilt!

    char *p;  //zeigt nach nirvana
    
    // p wurde wure noch keine adresse (new) zugewissen
    
    if(p)    //nirvana != NULL 
     delete p;
    


  • BorisDieKlinge schrieb:

    nicht unbeding, wenn du denn zeiger bei der deklaration nicht auf NULL initialisierst, kannst du nich prüfen ob er gilt!

    Aber genau das tut er doch, deshalb versteh ich nicht so ganz, was jetzt seine konkrete Frage ist..



  • aja .. pack das alloc des Heap mal in den {c-tor} deiner struct ...



  • nur mal so als hinweis:

    if (p)
     delete p;
    

    bewirkt das gleiche wie

    delete p;
    

    mit dem unterschied, dass nur einmal geschaut wird, ob der pointer!=0 ist.
    oder anders: delete schaut selber, ob der pointer 0 ist, daher ist eine vorherige prüfung nur überflüßiger code. es kann aber nicht kontrollieren, ob der pointer ins nirvana zeigt, aber das kann deine prüfung auch nicht


  • Mod

    ghorst schrieb:

    nur mal so als hinweis:

    if (p)
     delete p;
    

    bewirkt das gleiche wie

    delete p;
    

    mit dem unterschied, dass nur einmal geschaut wird, ob der pointer!=0 ist.
    oder anders: delete schaut selber, ob der pointer 0 ist, daher ist eine vorherige prüfung nur überflüßiger code. es kann aber nicht kontrollieren, ob der pointer ins nirvana zeigt, aber das kann deine prüfung auch nicht

    "bewirkt das Gleiche == bewirkt nichts" - bezogen auf die Speicherfreigabe besteht tatsächlich kein Unterschied soweit es die Deallokationsfunktion der Standardbibliothek betrifft. Es bedeutet nicht unbedingt, dass das beobachtbare Verhalten gleich ist. Ein solche Toleranz gegenüber Nullpointern wird eigenen Deallokationsfunktionen nicht explizit vorgeschrieben (iirc ist das aber jedenfalls für die globale Deallokationsfunktion ein Defekt, der in C++0x behoben wird - hab aber jetzt keine Lust nachzuschauen).
    Logisch unschön ist an einem einfachen delete ohne Prüfung, dass damit die Parität zwischen erfolgreicher Allokation und Deallokation aufgehoben wird - das macht es z.B. unmöglich, das Vorhandensein von Speicherlecks durch simplen Vergleich der Anzahl der Aufrufe beider Funktionen auszuschließen. Zudem bedeutet der Verzicht auf diese Prüfung unter Umständen den Verzicht auf Optimierungsmöglichkeiten für den Compiler. Ein schönes Anschauungsbeispiel dafür ist auto_ptr: dessen Destruktor zestört ja das durch auto_ptr referenzierte Objekt - sehr häufig ist dieser Zeiger aber bereits NULL unmittelbar nach einer Zuweisung - dann kann der Compiler den Destruktoraufruf völlig eliminieren. Das führt zudem dazu, dass der Compiler wissen kann, dass an dieser Stelle keine Exception auftreten wird (delete könnte theoretisch eine Exception werfen - auch wenn das eine ganz schlechte Idee ist) was z.B. beim Inlining von Vorteil ist.

    Im Endeffekt spielt es keine große praktische Rolle - allerdings kann ich daraus keine Präferenz für das Weglassen des if ableiten. Das ist ganz ähnlich wie das return 0, dass main in main weglassen darf - diese Möglichkeit stellt keinen echten Vorteil dar.

    Und nicht zuletzt haben rohe Zeiger ohnehin nichts mit dem Besitz von Speicher zu tun - aus dieser Sicht sind sowieso beide Varianten falsch.



  • wenn ich nicht völlig daneben liege, ist das verhalten, dass bei einem null-pointer durch delete nichts getan wird, im c++-standard vorgeschrieben.
    speicherlecks kannst du durch vergleich von new und delete eh nicht sinnvoll finden, sondern solltest besser ein sinnvolles tool einsetzen.

    auto_prt: wenn ich so in die gnu-c++-lib schauen, so wird im auto_prt nicht überprüft, ob der null ist, sondern einfach delete aufgerufen. wozu sollte man auch vorher prüfen? ob der compiler eine oder zwei identische prüfungen wegoptimiert ist vernachlässigbar. sie fällt sogar im zweifelsfall zu gunsten der einen prüfung nämlich der _in_ delete aus.


  • Mod

    ghorst schrieb:

    wenn ich nicht völlig daneben liege, ist das verhalten, dass bei einem null-pointer durch delete nichts getan wird, im c++-standard vorgeschrieben.

    völlig daneben liegst du nicht - aber die ganze Wahrheit ist es eben auch nicht.

    ISO/IEC 14882:2003(E) schrieb:

    The value of the first argument supplied to one of the deallocation functions provided in the standard library may be a null pointer value; if so, the call to the deallocation function has no effect.

    Das ist eine Aussage über das Verhalten der Deallokationsfunktion (und dann nur das der mit der Standardbibliothek gelieferten Version), nicht des delete-Operators - ein delete (T*)0 führt immer zum Aufruf der entsprechenden Deallokationsfunktion.

    speicherlecks kannst du durch vergleich von new und delete eh nicht sinnvoll finden, sondern solltest besser ein sinnvolles tool einsetzen.

    Sicherlich ist so ein Vergleich kein Ersatz für entsprechende Tools - andererseits ist ein einfacher Indikator dafür, dass etwas fehlerhaft sein könnte. Warum sich also dieser Möglichkeit ohne Not berauben?

    auto_prt: wenn ich so in die gnu-c++-lib schauen, so wird im auto_prt nicht überprüft, ob der null ist, sondern einfach delete aufgerufen.

    Visual C++ dagegen prüft es. Es ist ja nicht falsch, die Prüfung auszulassen - möglicherweise profitiert g++ auch einfach nicht an dieser Stelle. Solche Dinge sind ja notwendig compilerspezifisch. Ich erinnere mich mal einen Vergleich hinsichtlich optimierten Codes von Visual C++ 6.0 (ist also schon älter) gesehen zu haben, wo der Laufzeitunterschied signifikant war - das mag immer noch so sein oder nicht. Jedenfalls kann so eine Prüfung aus Optimierungssicht vorteilhaft sein, wenn der Fall p=0 hinreichend häufig auftritt (und bei auto_ptr ist das ja leicht die Mehrzahl der Fälle). Andererseits will ich auf diesem Punkt auch nicht herumreiten - solche Dinge sind per se stark vom Compiler und dem use case abhängig.

    wozu sollte man auch vorher prüfen? ob der compiler eine oder zwei identische prüfungen wegoptimiert ist vernachlässigbar. sie fällt sogar im zweifelsfall zu gunsten der einen prüfung nämlich der _in_ delete aus.

    Tatsächlich geht es nicht um die Eliminierung einer oder zweier Überprüfungen - die in der Regel insignifikant sein dürften - sondern die Vermeidung des teuren Aufrufs einer Funktion.

    Im Endeffekt spielt es sowieso keine Rolle. Resourcen gehören gemanaged: damit ist das ganze ausschließlich ein Implementationsdetail von Smartpointer. Und ich kann immer noch keinen Vorteil sehen, der für die Vermeidung des ifs spricht.



  • kurz und knapp: Man sollte den Zeiger anfangs immer auf NULL initialisieren! wenn ihm dann druch best. umstände kein speicher mit new zugewissen wird, kann bei delete nichts passieren..


  • Mod

    BorisDieKlinge schrieb:

    kurz und knapp: Man sollte den Zeiger anfangs immer auf NULL initialisieren! wenn ihm dann druch best. umstände kein speicher mit new zugewissen wird, kann bei delete nichts passieren..

    man sollte überhaupt (fast) immer alles initialisieren.



  • Entschuldigt bitte - ich bin jetzt etwas verwirrt, heißt das jetzt, dass es keine Sinn macht mit if (p) delete abzufragen oder doch? Ich setze jeden Pointer immer zuerst auf NULL.

    Danke für Aufklärung



  • Richtig erkannt - wenn du die Zeiger sauber initialisierst, stört sich delete überhaupt nicht daran ( delete NULL; ist legal und macht gar nichts), wenn du sie uninitialisiert lässt, bringt die Sicherheitsabfrage nichts - und es kracht trotzdem.



  • Ich würde sagen das Fazit dieses Threads ist:
    Es schadet nicht, delete auf einen Nullzeiger anzuwenden, es kann aber einen Funktionsaufruf sparen, vorher zu prüfen. Da Funktionsaufrufe im Vergleich zu einer Prüfung teuer sind, kann sich das lohnen, wenn so ein Codestück oft durchlaufen wird, und der Zeiger dabei oft Null ist.

    Letztlich gilt wohl, wie immer: Im Zweifel selbst messen.



  • delete ist keine wirkliche funktion, sondern wird immer (oder besser: mir ist keine variante bekannt) inline realisiert, daher ist die argumentation mit dem funktionsaufruf hinfällig.


  • Mod

    ghorst schrieb:

    delete ist keine wirkliche funktion, sondern wird immer (oder besser: mir ist keine variante bekannt) inline realisiert, daher ist die argumentation mit dem funktionsaufruf hinfällig.

    Was das eine mit dem anderen zu tun? delete zerstört das Objekt, auf das das Zeigerargument zeigt, ruft also ggf. den Destruktor auf. Danach wir die jeweilige Deallokationsfunktion (::operator delete bzw. Klassenspezifisch) aufgerufen.
    Im Falle eines NULL-Zeigers entfällt der Destruktoraufruf, ich habe allerdings noch nie gesehen, dass die Deallokationsfunktion geinlined wird, schließlich wird erst beim Linken klar, welche Funktion das sein wird (eine Eigene oder die der Standardbibliothek). Aus diesem Grunde kann dieser Aufruf auch nicht (bzw. nur schwer) eliminiert werden. Immerhin, die Performancefrage ist im Allgemeinen von untergeordneter Bedeutung. Persönlich bin ich der Ansicht, dass delete p ohne if (sofern NULL-Zeiger tatsächlich auftreten) nicht richtig spricht:
    "Zerstöre *p und gib den Speicher frei, oder auch nicht" - ist in meinen Augen unnötig verwirrend. Und zudem muss, wie bereits gesagt, eine selbstdefinierte Deallokationsfunktion NULL-Pointer nicht tolerieren.



  • es ging darum, dass man sich eine funktionsaufruf spart, wenn man erst if und dann delete aufruft. das bringt aber keinerlei vorteile, weil delete selber keine funktion ist sondern, wie du völlig richtig schreibst, erst der aufruf des dtors ein echter funktionsaufruf ist.

    ansonsten: ich finde es nicht verwirrend, wenn man delete ohne if schreibt. ist aus meiner sicht eigentlich sogar logischer, aber das ist wohl meine persönliche auffassung.



  • Hi,

    ich denke, es geht hier nicht nur um Performance (das aber auch), sondern auch um eine "implizite Statusangabe".

    int p = 0;
       bool habe_p_etwas_zugewiesen = false;
       // mach was ...
    
       if(Bedingung) {
          p = 3;
          habe_p_etwas_zugewiesen = true;
          // mach etwas
       }
    
       if(habe_p_etwas_zugewiesen) {
          // tue nur dann etwas, wenn p etwas zugewiesen wurde
       }
    

    Diese Struktur kann man bei Pointern ein wenig verkürzen, indem man die "habe_p_etwas_zugewiesen"-Information direkt in den Pointer "verlagert" (immer in der berechtigten Hoffnung, dass sich an der Adresse 0 niemals etwas befinden wird, auf das p zeigen soll).

    A* p = 0; // p zeigt "nirgendwohin"; Zeiger kann sich das "selbst merken"
       // mach was ...
    
       if(Bedingung) {
          p = ...// "new A;" oder "&a;" oder ...
          // mach etwas
       }
    
       if(p) {
          // tue nur dann etwas, wenn p auf etwas zeigt
       }
    

    Diese fachliche Struktur aufrecht zu erhalten, kann ein "Wert an sich" sein ... und man darf nicht vergessen, dass vielleicht nicht nur delete genutzt werden soll.

    Gruß,

    Simon2.



  • BorisDieKlinge schrieb:

    kurz und knapp: Man sollte den Zeiger anfangs immer auf NULL initialisieren!

    Nein. Man sollte einen Zeiger anfangs immer gleich mit dem korrekten Wert initialisieren, wenn möglich. Nur, wenn dies wirklich (!) nicht möglich ist, ergibt eine Initialisierung mit 0 Sinn. Und in den meisten Fällen, wo es unterlassen wird, wäre es möglich gewesen.

    Aber das ist alles eh Makulator: Verwendet keine rohen Zeiger sondern Smartpointer.


Anmelden zum Antworten