Ist es erlaubt, delete this;
wenn die delete-Anweisung die letzte Anweisung ist, die auf dieser Instanz der Klasse ausgeführt wird? Natürlich bin ich mir sicher, dass das durch den this
Zeiger dargestellte Objekt nur new
erstellt wurde.
Ich denke über so etwas nach:
void SomeModule::doStuff()
{
// in the controller, "this" object of SomeModule is the "current module"
// now, if I want to switch over to a new Module, eg:
controller->setWorkingModule(new OtherModule());
// since the new "OtherModule" object will take the lead,
// I want to get rid of this "SomeModule" object:
delete this;
}
Kann ich das tun?
c++
memory-management
new-operator
delete-operator
self-destruction
Martijn Courteaux
quelle
quelle
delete this
eine enge Kopplung zwischen der Klasse und der Zuordnungsmethode erstellt haben, die zum Erstellen von Objekten dieser Klasse verwendet wird. Das ist ein sehr schlechtes OO-Design, da das Grundlegendste in OOP darin besteht, autonome Klassen zu erstellen, die nicht wissen oder sich nicht darum kümmern, was ihr Anrufer tut. Daher sollte eine richtig gestaltete Klasse nicht wissen oder sich darum kümmern, wie sie zugewiesen wurde. Wenn Sie aus irgendeinem Grund einen so besonderen Mechanismus benötigen, wäre es meines Erachtens besser, eine Wrapper-Klasse um die eigentliche Klasse herum zu verwenden und den Wrapper mit der Zuordnung befassen zu lassen.setWorkingModule
?Antworten:
Die C ++ FAQ Lite hat einen speziellen Eintrag dafür
Ich denke, dieses Zitat fasst es gut zusammen
quelle
Ja,
delete this;
hat Ergebnisse definiert, solange Sie (wie Sie bemerkt haben) sicherstellen, dass das Objekt dynamisch zugewiesen wurde, und (natürlich) niemals versuchen, das Objekt zu verwenden, nachdem es zerstört wurde. Im Laufe der Jahre wurden viele Fragen dazu gestellt, was der Standard speziell sagtdelete this;
, anstatt einen anderen Zeiger zu löschen. Die Antwort darauf ist ziemlich kurz und einfach: Sie sagt nicht viel aus. Es heißt nur, dassdelete
der Operand ein Ausdruck sein muss, der einen Zeiger auf ein Objekt oder ein Array von Objekten bezeichnet. Es geht ziemlich detailliert auf Dinge ein, wie es herausfindet, welche (wenn überhaupt) Freigabefunktion aufgerufen werden muss, um den Speicher freizugeben, aber der gesamte Abschnitt überdelete
(§ [expr.delete]) wird überhaupt nichtdelete this;
speziell erwähnt. Der Abschnitt über Zerstörer erwähntdelete this
an einem Ort (§ [class.dtor] / 13):Dies stützt tendenziell die Idee, dass der Standard als
delete this;
gültig erachtet wird - wenn er ungültig wäre, wäre sein Typ nicht aussagekräftig.delete this;
Soweit ich weiß, ist dies der einzige Ort, den der Standard überhaupt erwähnt .Wie auch immer, einige denken über
delete this
einen bösen Hack nach und sagen jedem, der zuhört, dass er vermieden werden sollte. Ein häufig genanntes Problem ist die Schwierigkeit, sicherzustellen, dass Objekte der Klasse immer nur dynamisch zugewiesen werden. Andere halten es für eine absolut vernünftige Redewendung und verwenden es ständig. Persönlich bin ich irgendwo in der Mitte: Ich benutze es selten, aber zögern Sie nicht, es zu tun, wenn es das richtige Werkzeug für den Job zu sein scheint.Sie verwenden diese Technik hauptsächlich mit einem Objekt, das ein Leben hat, das fast ausschließlich seinem eigenen entspricht. Ein Beispiel, das James Kanze angeführt hat, war ein Abrechnungs- / Nachverfolgungssystem, an dem er für eine Telefongesellschaft gearbeitet hat. Wenn Sie anfangen zu telefonieren, nimmt etwas dies zur Kenntnis und erstellt ein
phone_call
Objekt. Ab diesem Zeitpunkt verarbeitet dasphone_call
Objekt die Details des Telefonanrufs (Herstellen einer Verbindung beim Wählen, Hinzufügen eines Eintrags zur Datenbank, um anzuzeigen, wann der Anruf gestartet wurde, möglicherweise Verbinden weiterer Personen, wenn Sie eine Telefonkonferenz führen usw.) Wannphone_call
Wenn die letzten Personen des Anrufs auflegen, führt das Objekt die endgültige Buchführung durch (z. B. fügt der Datenbank einen Eintrag hinzu, der angibt, wann Sie aufgelegt haben, damit sie berechnen können, wie lange Ihr Anruf gedauert hat), und zerstört sich dann selbst. Die Lebensdauer desphone_call
Das Objekt basiert darauf, wann die erste Person den Anruf startet und wann die letzten Personen den Anruf verlassen - aus Sicht des restlichen Systems ist es im Grunde genommen völlig willkürlich, sodass Sie es nicht an einen lexikalischen Bereich im Code binden können oder irgendetwas in dieser Reihenfolge.Für alle, denen es wichtig sein könnte, wie zuverlässig diese Art der Codierung sein kann: Wenn Sie nach, von oder durch fast jeden Teil Europas telefonieren, besteht eine gute Chance, dass sie (zumindest teilweise) per Code verarbeitet wird das macht genau das.
quelle
bool selfDelete
Parameter in den Konstruktor aufzunehmen, der einer Mitgliedsvariablen zugewiesen wird. Zugegeben, dies bedeutet, dem Programmierer genug Seil zu geben, um eine Schlinge darin zu binden, aber ich finde, dass dies Speicherlecks vorzuziehen ist.this
. Ja, der Code wird von der genau behandeltthis
. ;)Wenn es Ihnen Angst macht, gibt es einen vollkommen legalen Hack:
Ich denke, es
delete this
ist idiomatisches C ++, und ich präsentiere dies nur als eine Kuriosität.Es gibt einen Fall, in dem dieses Konstrukt tatsächlich nützlich ist - Sie können das Objekt löschen, nachdem Sie eine Ausnahme ausgelöst haben, die Mitgliedsdaten vom Objekt benötigt. Das Objekt bleibt bis nach dem Wurf gültig.
Hinweis: Wenn Sie einen Compiler verwenden älter als C ++ 11 Sie verwenden können ,
std::auto_ptr
stattstd::unique_ptr
, wird es das gleiche tun.quelle
unique_ptr
von einem anderenunique_ptr
erfordert einen Zug, jedoch nicht von einem rohen Zeiger. Es sei denn, die Dinge haben sich in C ++ 17 geändert?Einer der Gründe, warum C ++ entwickelt wurde, war die einfache Wiederverwendung von Code. Im Allgemeinen sollte C ++ so geschrieben werden, dass es funktioniert, unabhängig davon, ob die Klasse auf dem Heap, in einem Array oder auf dem Stapel instanziiert wird. "Delete this" ist eine sehr schlechte Codierungspraxis, da es nur funktioniert, wenn eine einzelne Instanz auf dem Heap definiert ist. und es sollte besser keine weitere Löschanweisung geben, die normalerweise von den meisten Entwicklern verwendet wird, um den Heap zu bereinigen. Dabei wird auch davon ausgegangen, dass in Zukunft kein Wartungsprogrammierer einen falsch wahrgenommenen Speicherverlust durch Hinzufügen einer Löschanweisung beheben kann.
Selbst wenn Sie im Voraus wissen, dass Ihr aktueller Plan darin besteht, nur eine einzige Instanz auf dem Heap zuzuweisen, was ist, wenn in Zukunft ein Happy-Go-Lucky-Entwickler hinzukommt und beschließt, eine Instanz auf dem Stack zu erstellen? Oder was ist, wenn er bestimmte Teile der Klasse ausschneidet und in eine neue Klasse einfügt, die er auf dem Stapel verwenden möchte? Wenn der Code "dies löschen" erreicht, wird er gelöscht und gelöscht. Wenn das Objekt jedoch den Gültigkeitsbereich verlässt, wird der Destruktor aufgerufen. Der Destruktor versucht dann erneut, es zu löschen, und dann werden Sie abgespritzt. In der Vergangenheit musste so etwas nicht nur das Programm, sondern auch das Betriebssystem und den Computer neu starten. In jedem Fall wird dies NICHT empfohlen und sollte fast immer vermieden werden. Ich müsste verzweifelt sein, ernsthaft verputzt,
quelle
Es ist erlaubt (benutze das Objekt danach einfach nicht mehr), aber ich würde solchen Code in der Praxis nicht schreiben. Ich denke, das
delete this
sollte nur in Funktionen erscheinen, die aufgerufen habenrelease
oderRelease
und so aussehen :void release() { ref--; if (ref<1) delete this; }
.quelle
Nun, in COM (Component Object Model)
delete this
kann die Konstruktion ein Teil derRelease
Methode sein, die aufgerufen wird, wenn Sie ein gewünschtes Objekt freigeben möchten:quelle
Dies ist die Kernsprache für Objekte mit Referenzzählung.
Das Referenzzählen ist eine starke Form der deterministischen Speicherbereinigung. Es stellt sicher, dass Objekte ihre EIGENE Lebensdauer verwalten, anstatt sich auf „intelligente“ Zeiger usw. zu verlassen, um dies für sie zu tun. Auf das zugrunde liegende Objekt wird immer nur über intelligente "Referenz" -Zeiger zugegriffen, die so ausgelegt sind, dass die Zeiger eine Element-Ganzzahl (den Referenzzähler) im tatsächlichen Objekt inkrementieren und dekrementieren.
Wenn die letzte Referenz vom Stapel fällt oder gelöscht wird, geht der Referenzzähler auf Null. Das Standardverhalten Ihres Objekts ist dann ein Aufruf zum "Löschen" zur Speicherbereinigung. Die von mir geschriebenen Bibliotheken bieten einen geschützten virtuellen "CountIsZero" -Aufruf in der Basisklasse, damit Sie dieses Verhalten für Dinge wie Caching überschreiben können.
Der Schlüssel, um dies sicher zu machen, besteht nicht darin, Benutzern den Zugriff auf den CONSTRUCTOR des betreffenden Objekts zu ermöglichen (es geschützt zu machen), sondern sie dazu zu bringen, ein statisches Mitglied - das FACTORY-ähnliche "static Reference CreateT (...)" - aufzurufen. Auf diese Weise wissen Sie sicher, dass sie immer mit gewöhnlichen "neuen" erstellt werden und dass kein roher Zeiger verfügbar ist, sodass "dies löschen" niemals explodieren wird.
quelle
Sie können dies tun. Sie können dies jedoch nicht zuordnen. Der Grund, warum Sie dies angeben: "Ich möchte die Ansicht ändern", erscheint daher sehr fraglich. Die bessere Methode wäre meiner Meinung nach, dass das Objekt, das die Ansicht enthält, diese Ansicht ersetzt.
Natürlich verwenden Sie RAII-Objekte und müssen daher eigentlich gar nicht delete aufrufen ... richtig?
quelle
Dies ist eine alte, beantwortete Frage, aber @Alexandre fragte: "Warum sollte jemand dies tun wollen?", Und ich dachte, ich könnte eine Beispielverwendung bereitstellen, die ich heute Nachmittag in Betracht ziehe.
Legacy-Code. Verwendet nackte Zeiger Obj * obj mit einem Löschobjekt am Ende.
Leider muss ich manchmal, nicht oft, das Objekt länger am Leben halten.
Ich denke darüber nach, es zu einem intelligenten Zeiger mit Referenzzählung zu machen. Aber es gäbe viel Code zu ändern, wenn ich ihn
ref_cnt_ptr<Obj>
überall verwenden würde. Und wenn Sie nacktes Obj * und ref_cnt_ptr mischen, können Sie das Objekt implizit löschen lassen, wenn das letzte ref_cnt_ptr verschwindet, obwohl Obj * noch am Leben ist.Ich denke also darüber nach, ein explizites_delete_ref_cnt_ptr zu erstellen. Dh ein Referenzzählzeiger, bei dem das Löschen nur in einer expliziten Löschroutine erfolgt. Verwenden Sie es an einer Stelle, an der der vorhandene Code die Lebensdauer des Objekts kennt, sowie in meinem neuen Code, der das Objekt länger am Leben hält.
Das Inkrementieren und Dekrementieren des Referenzzählers als explizit_delete_ref_cnt_ptr wird manipuliert.
Aber NICHT freigeben, wenn der Referenzzähler im Destruktor explic_delete_ref_cnt_ptr Null ist.
Nur freigeben, wenn der Referenzzähler in einer expliziten löschähnlichen Operation als Null angezeigt wird. ZB in so etwas wie:
OK, so etwas. Es ist etwas ungewöhnlich, dass ein Zeigertyp mit Referenzzählung das Objekt, auf das im rc'ed ptr-Destruktor verwiesen wird, nicht automatisch löscht. Aber es scheint, als würde dies das Mischen von nackten Zeigern und rc'ed-Zeigern etwas sicherer machen.
Bisher ist dies jedoch nicht erforderlich.
Aber dann kam mir der Gedanke: Wenn das Objekt, auf das der Pointee zeigt, weiß, dass es als Referenz gezählt wird, z. B. wenn sich die Zählung innerhalb des Objekts (oder in einer anderen Tabelle) befindet, könnte die Routine delete_if_rc0 eine Methode der sein Pointee-Objekt, nicht der (intelligente) Zeiger.
Eigentlich muss es überhaupt keine Mitgliedsmethode sein, könnte aber eine freie Funktion sein:
(Übrigens, ich weiß, dass der Code nicht ganz richtig ist - er wird weniger lesbar, wenn ich alle Details hinzufüge, also lasse ich ihn so.)
quelle
Löschen ist zulässig, solange sich das Objekt im Heap befindet. Sie müssten verlangen, dass das Objekt nur ein Heap ist. Die einzige Möglichkeit, dies zu tun, besteht darin, den Destruktor zu schützen. Auf diese Weise kann delete NUR aus der Klasse aufgerufen werden, sodass Sie eine Methode benötigen, die das Löschen sicherstellt
quelle