Warum setzt delete den Zeiger nicht auf NULL?


127

Ich habe mich immer gefragt, warum die automatische Einstellung des Zeigers auf NULL nach dem Löschen nicht zum Standard gehört. Wenn dies behoben wird, treten viele der Abstürze aufgrund eines ungültigen Zeigers nicht auf. Ich kann mir jedoch einige Gründe vorstellen, warum der Standard dies eingeschränkt hätte:

  1. Performance:

    Eine zusätzliche Anweisung kann die deleteLeistung beeinträchtigen.

  2. Könnte es an constZeigern liegen.

    Andererseits hätte Standard wohl etwas für diesen speziellen Fall tun können.

Kennt jemand genaue Gründe dafür, dies nicht zuzulassen?

Antworten:


150

Stroustrup selbst antwortet. Ein Ausschnitt:

C ++ erlaubt explizit eine Implementierung von delete, um einen lvalue-Operanden auf Null zu setzen, und ich hatte gehofft, dass Implementierungen dies tun würden, aber diese Idee scheint bei Implementierern nicht populär geworden zu sein.

Das Hauptproblem, das er aufwirft, ist jedoch, dass das Argument von delete kein Wert sein muss.


Ich denke, das könnte etwas mehr Erklärung gebrauchen. Ich bin mir nicht mal sicher, was er sagt ... Ich denke, ich muss zu einem späteren Zeitpunkt zurückkommen, wenn ich ein paar Stunden damit verbringen kann, dies zu untersuchen, bis ich es bekomme. Oder Sie können die Antwort erläutern, um uns ein besseres Verständnis zu ermöglichen.
Gabriel Staples

63

Erstens würde das Setzen auf Null eine im Speicher gespeicherte Variable erfordern. Es ist wahr, dass Sie normalerweise einen Zeiger in einer Variablen haben, aber manchmal möchten Sie vielleicht ein Objekt an einer gerade berechneten Adresse löschen . Das wäre unmöglich mit "annullieren" löschen.

Dann kommt die Leistung. Möglicherweise haben Sie den Code so geschrieben, dass der Zeiger unmittelbar nach dem Löschen den Gültigkeitsbereich verlässt . Es ist nur Zeitverschwendung, es mit Null zu füllen. Und C ++ ist eine Sprache mit der Ideologie "Brauchen Sie es nicht? Dann müssen Sie nicht dafür bezahlen".

Wenn Sie Sicherheit benötigen, stehen Ihnen zahlreiche intelligente Zeiger zur Verfügung, oder Sie können Ihre eigenen schreiben - besser und intelligenter.


4
Guter Punkt für berechnete Adresse, auch wenn es etwas ist, das Sie nicht oft sehen
Snemarch

Sprechen Sie über eine neue Platzierung, wenn Sie sagen, dass Sie manchmal ein Objekt an einer gerade berechneten Adresse löschen möchten? ???
Zerstörer

@PravasiMeet Nein, ich meine so etwas wiedelete (ptr + i)
Scharfzahn

39

Sie können mehrere Zeiger haben, die auf diesen Speicher zeigen. Es würde ein falsches Sicherheitsgefühl erzeugen, wenn der Zeiger, den Sie für das Löschen angegeben haben, auf null gesetzt würde, alle anderen Zeiger jedoch nicht. Ein Zeiger ist nichts anderes als eine Adresse, eine Zahl. Es könnte genauso gut ein Int mit einer Dereferenzierungsoperation sein. Mein Punkt ist, dass Sie auch jeden einzelnen Zeiger scannen müssten, um diejenigen zu finden, die auf denselben Speicher verweisen, den Sie gerade gelöscht haben, und sie ebenfalls auf Null setzen. Es wäre rechenintensiv, alle Zeiger nach dieser Adresse zu durchsuchen und sie auf Null zu setzen, da die Sprache nicht dafür ausgelegt ist. (Obwohl einige andere Sprachen ihre Referenzen so strukturieren, dass ein ähnliches Ziel auf andere Weise erreicht wird.)


19

Ein Zeiger kann in mehr als einer Variablen gespeichert werden. Wenn Sie einen dieser Zeiger auf NULL setzen, bleiben in den anderen Variablen immer noch ungültige Zeiger übrig. Sie gewinnen also nicht wirklich viel, sondern schaffen eher ein falsches Sicherheitsgefühl.

Außerdem können Sie Ihre eigene Funktion erstellen, die das tut, was Sie wollen:

template<typename T>
void deleten(T *&ptr) {
  delete ptr;
  ptr = NULL;
}

12

Weil es nicht wirklich nötig ist und weil es das Löschen erfordern würde, Zeiger zu Zeiger zu nehmen und nicht nur Zeiger.


stimmt, aber das würde zu demselben Overhead führen
Snemarch

7

deletewird hauptsächlich in Destruktoren verwendet. In diesem Fall ist es sinnlos, ein Mitglied auf NULL zu setzen. Einige Zeilen später, zum Abschluss }, existiert das Mitglied nicht mehr. Bei Zuweisungsoperatoren folgt auf eine Löschung normalerweise ohnehin eine Zuweisung.

Außerdem würde der folgende Code illegal sein:

T* const foo = new T;
delete foo;

6

Hier ist ein weiterer Grund; Angenommen, delete setzt sein Argument auf NULL:

int *foo = new int;
int *bar = foo;
delete foo;

Sollte die Leiste auf NULL gesetzt werden? Können Sie das verallgemeinern?


5

Wenn Sie über ein Array von Zeigern verfügen und Ihre zweite Aktion darin besteht, das leere Array zu löschen, ist es sinnlos, jeden Wert auf Null zu setzen, wenn der Speicher freigegeben werden soll. Wenn du willst, dass es null ist, schreibe null darauf :)


4

In C ++ können Sie Ihren eigenen Operator neu definieren und löschen, damit er beispielsweise Ihren eigenen Pool-Allokator verwendet. Wenn Sie dies tun, ist es möglich, new zu verwenden und mit Dingen zu löschen, die nicht ausschließlich Adressen sind, sondern Indizes in Ihrem Pool-Array enthalten. In diesem Zusammenhang kann der Wert von NULL (0) eine rechtliche Bedeutung haben (bezogen auf das erste Element im Pool).
Wenn also delete NULL automatisch auf sein Argument gesetzt hat, hat dies nicht immer die Bedeutung von - setze den Wert auf einen ungültigen Wert. Der ungültige Wert ist möglicherweise nicht immer NULL.


4

Die Philosophie von C ++ lautet "Bezahlen Sie nur, wenn Sie es verwenden". Ich denke, es könnte Ihre Frage beantworten.

Manchmal könnten Sie auch einen eigenen Heap haben, der gelöschten Speicher wiederherstellt. Oder manchmal einen Zeiger, der keiner Variablen gehört. Oder Zeiger in wenigen Variablen gespeichert - es ist möglich, nur eine davon auf Null zu setzen.
Wie Sie sehen können, gibt es viele Probleme und mögliche Probleme.


3

Das automatische Setzen des Zeigers auf NULL würde die meisten Probleme mit einer schlechten Zeigernutzung nicht lösen. Der einzige Absturz, den es vermeiden würde, ist, wenn Sie versuchen, es zweimal zu löschen. Was ist, wenn Sie eine Member-Funktion für einen solchen Zeiger aufrufen? Es würde immer noch abstürzen (vorausgesetzt, es greift auf Mitgliedsvariablen zu). C ++ hindert Sie nicht daran, Funktionen für NULL-Zeiger aufzurufen, und sollte dies auch nicht unter Leistungsgesichtspunkten tun.


-1

Ich sehe Leute, die seltsame Antworten auf diese Frage geben.

ptr = NULL; Wie kann eine so einfache Aussage zu Leistungsverzögerungen führen?

Eine andere Antwort lautet, dass mehrere Zeiger auf denselben Speicherort zeigen können. Sicher können wir. In diesem Fall würde eine Löschoperation an einem Zeiger nur diesen Zeiger NULL machen (wenn Löschen den Zeiger NULL macht) und der andere Zeiger wäre nicht NULL und würde auf den freien Speicherort zeigen.

Die Lösung hierfür sollte sein, dass der Benutzer alle Zeiger löschen sollte, die auf dieselbe Position zeigen. Intern sollte geprüft werden, ob der Speicher bereits freigegeben ist, als nicht freigegeben. Machen Sie nur den Zeiger NULL.

Stroustrup hätte das Löschen so gestalten können. Er dachte, Programmierer würden sich darum kümmern. Also ignorierte er es.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.