Frage

Ich sehe oft Legacy-Code für NULL Überprüfung vor einem Zeiger zu löschen, ähnlich wie,

if (NULL != pSomeObject) 
{
    delete pSomeObject;
    pSomeObject = NULL;
}

Gibt es einen Grund für einen NULL Zeiger auf zu prüfen, bevor sie zu löschen? Was ist der Grund für den Zeiger Einstellung danach auf NULL?

War es hilfreich?

Lösung

Es ist perfekt „sicher“ einen Null-Zeiger zu löschen; es beläuft sich effektiv auf eine no-op.

Der Grund, warum Sie wollen vielleicht für null überprüfen, bevor Sie löschen ist, dass ein Null-Zeiger zu löschen versucht, einen Fehler in Ihrem Programm hinweisen könnte.

Andere Tipps

Die C ++ Standard garantiert, dass es legal ist, einen Null-Zeiger in einem verwenden delete-Ausdruck (§8.5.2.5 / 2). Es ist jedoch nicht spezifiziert , ob dies eine Freigabe-Funktion (operator delete oder operator delete[]; §8.5.2.5 / 7, Anmerkung) nennen.

Wenn ein Standard Deallokation Funktion (d.h. durch die Standard-Bibliothek zur Verfügung gestellt) mit einem Null-Zeigern aufgerufen wird, dann hat der Aufruf keine Wirkung (§6.6.4.4.2 / 3).

Aber es ist nicht spezifiziert, was passiert, wenn die Freigabe-Funktion nicht durch die Standardbibliothek zur Verfügung gestellt wird - d. H, was passiert, wenn wir operator delete Überlastung (oder operator delete[])

Ein kompetenter Programmierer würde behandeln Null-Zeiger entsprechend innerhalb die Freigabe-Funktion, anstatt vor dem Aufruf, wie in OPs code.Likewise gezeigt, um den Zeiger Einstellung / nullptr auf NULL bereits nach der Löschung dient sehr begrenzter Zweck. Einige Leute mögen dies im Geist tun, von defensiver Programmierung : es Programmverhalten macht etwas mehr im Falle eines Fehlers vorhersehbar: den Zeiger nach dem Löschen Zugriff wird in einem null-Zeiger-Zugriff führen eher als ein Zugang zu einem zufälligen Speicherort. Obwohl beide Operationen nicht definiertes Verhalten sind, viel besser vorhersagbar in der Praxis das Verhalten eines Null-Pointer-Zugriff ist (es führt meist in einem direkten Absturz statt Speicherfehler). Da die Speicherverfälschungen sind besonders schwer zu debuggen, gelöschte Zeiger Hilfen Debuggen zurückgesetzt wird.

- Natürlich ist dies das Symptom behandeln, anstatt die Ursache (das heißt der Fehler). Sie behandeln sollen Zeiger als Codegeruch. Saubere, moderne C ++ Code Zurücksetzen wird Speicherbesitz klar und statisch geprüft machen (durch intelligente Zeiger oder gleichwertige Mechanismen) und damit beweisbar diese Situation vermeiden.

Bonus: Eine Erklärung überladener operator delete:

operator delete ist (trotz seines Namens) eine Funktion, die wie jede andere Funktion überlastet werden können. Diese Funktion wird intern für jeden Aufruf von operator delete mit passenden Argumenten aufgerufen. Das gleiche gilt für operator new.

Eine Überlastung operator new (und dann operator delete auch) macht Sinn, in einigen Situationen, wenn Sie genau zu steuern, wie Speicher zugeordnet ist. dies zu tun, ist nicht einmal sehr hart, aber einige Vorsichtsmaßnahmen, die die richtige Verhalten zu gewährleisten, erfolgen. Scott Meyers beschreibt dies im Detail Effective C ++ .

Für jetzt lassen Sie uns einfach sagen, dass wir die globale Version operator new für das Debuggen, um eine Überlastung möchten. Bevor wir dies tun, eine kurze Mitteilung über das, was in dem folgenden Code geschieht:

klass* pobj = new klass;
// … use pobj.
delete pobj;

Was passiert hier eigentlich? Nun, die oben kann in etwa den folgenden Code übersetzt werden:

// 1st step: allocate memory
klass* pobj = static_cast<klass*>(operator new(sizeof(klass)));
// 2nd step: construct object in that memory, using placement new:
new (pobj) klass();

// … use pobj.

// 3rd step: call destructor on pobj:
pobj->~klass();
// 4th step: free memory
operator delete(pobj);

Beachten Sie Schritt 2, wo wir new mit einer etwas seltsamen Syntax nennen. Dies ist ein Aufruf zu sogenannten Platzierung new , die eine Adresse nimmt und konstruiert ein Objekt an dieser Adresse. Dieser Operator kann auch überlastet werden. In diesem Fall dient es nur den Konstruktor der Klasse klass zu nennen.

Nun, ohne weitere Umschweife hier ist der Code für eine überladene Version der Operatoren:

void* operator new(size_t size) {
    // See Effective C++, Item 8 for an explanation.
    if (size == 0)
        size = 1;

    cerr << "Allocating " << size << " bytes of memory:";

    while (true) {
        void* ret = custom_malloc(size);

        if (ret != 0) {
            cerr << " @ " << ret << endl;
            return ret;
        }

        // Retrieve and call new handler, if available.
        new_handler handler = set_new_handler(0);
        set_new_handler(handler);

        if (handler == 0)
            throw bad_alloc();
        else
            (*handler)();
    }
}

void operator delete(void* p) {
    cerr << "Freeing pointer @ " << p << "." << endl;
    custom_free(p);
}

Dieser Code verwendet nur eine benutzerdefinierte Implementierung von malloc / free intern, da die meisten Implementierungen tun. Es schafft auch eine Debugging-Ausgabe. Betrachten Sie den folgenden Code ein:

int main() {
    int* pi = new int(42);
    cout << *pi << endl;
    delete pi;
}

Es ergab die folgende Ausgabe:

Allocating 4 bytes of memory: @ 0x100160
42
Freeing pointer @ 0x100160.

Nun, tut dieser Code etwas grundsätzlich anderes als die Standard-Implementierung von operator delete: Es dauerte nicht für Null-Zeiger testen Der Compiler prüft dies nicht so der obige Code kompiliert, aber es kann geben! böse Fehler zur Laufzeit, wenn Sie versuchen,löschen Null-Zeiger.

Aber, wie ich schon sagte, ist dieses Verhalten tatsächlich unerwartet und eine Bibliothek Schriftsteller sollte sorgt für Null-Zeiger in der operator delete zu überprüfen. Diese Version ist viel verbessert:

void operator delete(void* p) {
    if (p == 0) return;
    cerr << "Freeing pointer @ " << p << "." << endl;
    free(p);
}

Hieraus folgt, dass eine schlampige Implementierung von operator delete expliziten null Kontrollen im Client-Code erfordern kann, ist dies Nicht-Standard-Verhalten und nur in Legacy-Unterstützung toleriert werden soll (, wenn überhaupt ).

Löschen intern prüft, ob NULL. Ihr Test ist redundant

null Löschen ist ein No-op. Es gibt keinen Grund für null zu überprüfen, vor dem Aufruf löschen.

Sie möchten vielleicht aus anderen Gründen für nichtig überprüfen, ob der Zeiger NULL einige zusätzliche Informationen trägt, die Sie interessieren.

Nach C ++ 03 5.3.5 / 2, ist es sicher einen Null-Zeiger zu löschen. Die folgende wird aus dem Standard zitiert:

  

In beiden alternativen, wenn der Wert des Operanden der Lösch- ist die   Null-Zeiger-Betrieb hat keine Auswirkungen.

Wenn pSomeObject NULL ist, löschen Sie werden nichts tun. Also nein, Sie müssen nicht für NULL überprüfen.

Wir halten es für eine gute Praxis auf den Zeiger NULL zuweisen, nachdem es zu löschen, wenn es überhaupt möglich ist, dass einige knucklehead den Zeiger zu verwenden versuchen kann. einen NULL-Zeiger zu verwenden ist etwas besser als einen Zeiger auf mit wem weiß, was (der NULL-Zeiger wird zu einem Absturz führen, um den Zeiger auf gelöschte Speicher nicht)

Es gibt keinen Grund für NULL zu überprüfen, bevor zu löschen. NULL nach löschen zuweisen könnte erforderlich sein, wenn irgendwo in dem Code überprüft gemacht werden, ob ein Objekt bereits durch Ausführen einer NULL Prüfung zugeordnet ist. Ein Beispiel wäre eine Art von Cache-Daten sein, die bei Bedarf zugeordnet wird. Jedes Mal, wenn Sie klar aus dem Cache-Objekt, das Sie NULL auf den Zeiger zuweisen, damit der Code, der das Objekt zuordnet, weiß, dass es eine Zuordnung durchführen muss.

Ich glaube, die vorherigen Entwickler codiert es „redundant“ einige Millisekunden zu sparen: Es ist eine gute Sache zu haben, den Zeiger auf NULL gesetzt auf gelöscht werden, so könnten Sie eine Zeile wie die folgenden direkt nach dem Löschen des Objekts verwenden:

if(pSomeObject1!=NULL) pSomeObject1=NULL;

Aber dann löschen ist, dass genauer Vergleich tut sowieso (nichts zu tun, wenn es NULL ist). Warum zweimal das? Sie können immer pSomeObject auf NULL zuweisen, nachdem löscht Aufruf, unabhängig von seinem aktuellen Wert -. Aber das wäre etwas überflüssig, wenn es bereits diesen Wert hat

Also meine Wette ist der Autor dieser Zeilen versuchte pSomeObject1, um sicherzustellen, würde immer NULL sein, nachdem sie gelöscht werden, ohne dass die Kosten eines potenziell unnötigen Test und assignation entstehen.

Es hängt davon ab, was Sie tun. Einige ältere Implementierungen von free, zum Beispiel, wird nicht glücklich sein, wenn sie einen NULL Zeiger übergeben werden. Einige Bibliotheken haben immer noch dieses Problem. Zum Beispiel XFree in der Xlib-Bibliothek sagt:

  

Beschreibung

     

Die XFree-Funktion ist ein   Allzweck- Xlib Routine,   befreit die angegebenen Daten. Sie müssen   verwenden Sie es alle Objekte zu befreien, waren   durch Xlib zugeordnet, es sei denn, eine alternative   Funktion wird explizit angegeben für   das Objekt. Ein NULL-Zeiger kann nicht sein,   bestehen diese Funktion.

So betrachten NULL Zeiger als einen Fehler zu befreien und Sie werden sicher sein.

Was meine Beobachtungen, einen Null-Zeiger zu löschen verwenden löschen ist sicher in Unix-basierten Maschinen ike PARISC und itanium. Aber ist ziemlich unsicher für Linux-Systeme, da der Prozess dann abstürzen würde.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top