Ist “delete this” in C++ erlaubt?

Lesezeit: 11 Minuten

Ist delete this in C erlaubt
Martin Courteaux

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 Objekt durch die dargestellt wird this-Zeiger ist newly-erstellt.

Ich denke an so etwas:

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?

  • Hauptproblem wäre, wenn Sie delete this Sie haben eine enge Kopplung zwischen der Klasse und der Zuordnungsmethode erstellt, die zum Erstellen von Objekten dieser Klasse verwendet wird. Das ist sehr schlechtes OO-Design, da die grundlegendste Sache in OOP darin besteht, autonome Klassen zu erstellen, die nicht wissen oder sich nicht darum kümmern, was ihr Aufrufer tut. Daher sollte eine richtig entworfene Klasse nicht wissen oder sich darum kümmern, wie sie zugewiesen wurde. Wenn Sie aus irgendeinem Grund einen solchen besonderen Mechanismus benötigen, wäre es meiner Meinung nach besser, eine Wrapper-Klasse um die eigentliche Klasse herum zu verwenden und den Wrapper die Zuweisung erledigen zu lassen.

    – Ludin

    14. Oktober 2015 um 7:58 Uhr


  • Kannst du nicht löschen setWorkingModule?

    – Jimmy T.

    12. April 2020 um 10:35 Uhr

  • @Lundin CFrameWnd Klasse von MFC tut delete this; in PostNcDestroy denn dann wird die WinAPI-Klasse, die sie umschließt, vermutlich zerstört. Es hat also seine eigenen gültigen Anwendungsfälle, würde ich sagen.

    – Ayxan Haqverdili

    29. November 2020 um 18:42 Uhr

  • @Lundin Das Problem ist nicht die Freigabe, sondern die Zerstörung. In C++ besteht die einzig richtige Möglichkeit, diese beiden zu trennen und dennoch Kapselung und Polymorphismus zu erreichen, darin, shared_ptr zu verwenden. Unique_ptr trennt sie nicht. Die betreffende Klasse kümmert sich nicht um die Zuweisung/Aufhebung der Zuweisung, aber sie möchte ihre Lebensdauer kontrollieren. Ich würde wetten, dass die fragliche Klasse mit shared_ptr/enable_shared_from_this richtig entworfen werden kann, aber ich mag es nicht, dass es so gemacht werden muss, zumal shared_ptr/enable_shared_from_this viel Codegröße verbrauchen und daher für meine eingebettete Entwicklung unbrauchbar sind .

    – Dragan

    16. Juli 2021 um 10:49 Uhr

1647137416 522 Ist delete this in C erlaubt
JaredPar

Die C++ FAQ Lite hat einen eigenen Eintrag dafür

Ich denke, dieses Zitat fasst es gut zusammen

Solange Sie vorsichtig sind, ist es in Ordnung, wenn ein Objekt Selbstmord begeht (löschen Sie dies).

  • Die entsprechende FQA hat auch einige nützliche Kommentare: yosefk.com/c++fqa/heap.html#fqa-16.15

    – Alexander C.

    30. Juni 2010 um 15:51 Uhr


  • Aus Sicherheitsgründen können Sie den privaten Destruktor für das ursprüngliche Objekt verwenden, um sicherzustellen, dass es nicht auf dem Stapel oder als Teil eines Arrays oder Vektors erstellt wird.

    – Cem Kalyoncu

    29. September 2015 um 12:58 Uhr

  • Definiere ‘vorsichtig’

    – CinCout

    13. September 2017 um 12:24 Uhr

  • ‘Vorsicht’ ist im verlinkten FAQ-Artikel definiert. (Während der FQA-Link meistens – wie fast alles darin – schimpft, wie schlecht C++ ist)

    – CharonX

    3. Mai 2018 um 7:42 Uhr

1647137417 358 Ist delete this in C erlaubt
Jerry Sarg

Jawohl, delete this; hat definierte Ergebnisse, 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 die Norm konkret aussagt delete this;, im Gegensatz zum Löschen eines anderen Zeigers. Die Antwort darauf ist ziemlich kurz und einfach: Sie sagt nicht viel aus. Das sagt es einfach deleteDer Operand von muss ein Ausdruck sein, 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 den gesamten Abschnitt weiter delete (§[expr.delete]) nicht erwähnt delete this; konkret überhaupt. Der Abschnitt über Destruktor erwähnt delete this in einem Platz (§[class.dtor]/13):

Am Definitionspunkt eines virtuellen Destruktors (einschließlich einer impliziten Definition (15.8)) wird die Nicht-Array-Freigabefunktion bestimmt, als ob für den Ausdruck delete this in einem nicht virtuellen Destruktor der Klasse des Destruktors erscheint (siehe 8.3.5 ).

Dies unterstützt tendenziell die Idee, die der Standard berücksichtigt delete this; um gültig zu sein – wenn es ungültig wäre, wäre sein Typ nicht aussagekräftig. Das ist die einzige Stelle, die der Standard erwähnt delete this; überhaupt, soweit ich weiß.

Wie auch immer, einige denken darüber nach delete this ein fieser Hack, und sagen Sie 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 vollkommen vernünftige Redewendung und verwenden es ständig. Ich persönlich liege irgendwo in der Mitte: Ich benutze es selten, aber zögere nicht, es zu tun, wenn es das richtige Werkzeug für den Job zu sein scheint.

Am häufigsten verwenden Sie diese Technik bei einem Objekt, das ein fast völlig eigenes Leben hat. Ein Beispiel, das James Kanze genannt hat, war ein Abrechnungs-/Verfolgungssystem, an dem er für eine Telefongesellschaft gearbeitet hat. Wenn Sie anfangen zu telefonieren, nimmt etwas davon Notiz und erstellt einen phone_call Objekt. Ab diesem Zeitpunkt ist die phone_call Das Objekt verarbeitet die Details des Telefonanrufs (Herstellen einer Verbindung beim Wählen, Hinzufügen eines Eintrags zur Datenbank, der angibt, wann der Anruf begonnen hat, möglicherweise mehr Personen verbinden, wenn Sie eine Telefonkonferenz durchführen usw.) Wann die letzten Personen im Anruf sind auflegen, die phone_call Das Objekt führt seine endgültige Buchführung durch (z. B. fügt einen Eintrag zur Datenbank 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 der phone_call Objekt basiert darauf, wann die erste Person den Anruf startet und wann die letzten Personen den Anruf verlassen – aus der Sicht des restlichen Systems ist es im Grunde völlig willkürlich, also Sie kippen binden Sie es an einen beliebigen lexikalischen Bereich im Code oder irgendetwas in dieser Reihenfolge.

Für alle, die sich dafür interessieren, wie zuverlässig diese Art der Verschlüsselung sein kann: Wenn Sie in, aus oder durch fast alle Teile Europas telefonieren, besteht eine ziemlich gute Chance, dass dies (zumindest teilweise) per Code abgewickelt wird das macht genau das.

  • Danke, ich werde es irgendwo in meinem Gedächtnis ablegen. Ich nehme an, Sie definieren die Konstruktoren und Destruktoren als privat und verwenden eine statische Factory-Methode, um solche Objekte zu erstellen.

    – Alexander C.

    30. Juni 2010 um 18:47 Uhr

  • @Alexandre: Sie würden das wahrscheinlich in den meisten Fällen sowieso tun – ich kenne nicht annähernd alle Details des Systems, an dem er gearbeitet hat, daher kann ich es jedoch nicht mit Sicherheit sagen.

    – Jerry Sarg

    30. Juni 2010 um 20:27 Uhr

  • Ich umgehe oft das Problem, wie der Speicher zugewiesen wurde, indem ich a einfüge bool selfDelete Parameter im Konstruktor, der einer Mitgliedsvariablen zugewiesen wird. Zugegeben, das bedeutet, dem Programmierer genug Seil in die Hand zu geben, um eine Schlinge darin zu binden, aber ich finde das besser als Speicherlecks.

    – MBraedley

    14. September 2016 um 16:24 Uhr

  • @MBraedley: Ich habe dasselbe getan, vermeide aber lieber, was mir wie ein Kludge erscheint.

    – Jerry Sarg

    14. September 2016 um 17:22 Uhr

  • Für alle, die sich dafür interessieren … es besteht eine ziemlich gute Chance, dass es (zumindest teilweise) von Code gehandhabt wird, der genau das tut this. Ja, der Code wird von dem genau gehandhabt this. 😉

    – Galaxie

    7. Januar 2019 um 1:32 Uhr


Ist delete this in C erlaubt
Mark Lösegeld

Wenn es dir Angst macht, gibt es einen vollkommen legalen Hack:

void myclass::delete_me()
{
    std::unique_ptr<myclass> bye_bye(this);
}

Ich denke delete this ist jedoch idiomatisches C++, und ich präsentiere dies nur als 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 aus dem Objekt benötigt. Das Objekt bleibt gültig, bis der Wurf erfolgt ist.

void myclass::throw_error()
{
    std::unique_ptr<myclass> bye_bye(this);
    throw std::runtime_exception(this->error_msg);
}

Hinweis: Wenn Sie einen Compiler verwenden, der älter als C++11 ist, können Sie verwenden std::auto_ptr anstatt std::unique_ptres wird dasselbe tun.

  • Ich kann dies nicht mit c++ 11 kompilieren, gibt es dafür spezielle Compiler-Optionen? Erfordert es auch keine Bewegung des this-Zeigers?

    – Eule

    22. März 2017 um 12:19 Uhr

  • @Owl nicht sicher, was du meinst, es funktioniert für mich: ideone.com/aavQUK. Ein … Erstellen unique_ptr von Ein weiterer unique_ptr erfordert eine Bewegung, aber nicht von einem rohen Zeiger. Es sei denn, die Dinge haben sich in C++17 geändert?

    – Markieren Sie Lösegeld

    22. März 2017 um 15:40 Uhr


  • Ahh C++14, das wird der Grund sein. Ich muss mein C++ auf meiner Dev-Box aktualisieren. Ich werde es heute Abend noch einmal auf meinem kürzlich aufgetauchten Gentoo-System versuchen!

    – Eule

    30. März 2017 um 14:49 Uhr

  • Es ist ein Hack, es sei denn, Sie machen Ihren Destruktor privat, was dazu führt, dass unique_ptr nicht funktioniert.

    – Dragan

    16. Juli 2021 um 10:54 Uhr

1647137418 470 Ist delete this in C erlaubt
Bob Bryan

Einer der Gründe, warum C++ entwickelt wurde, war die einfache Wiederverwendung von Code. Im Allgemeinen sollte C++ so geschrieben werden, dass es funktioniert, egal ob die Klasse auf dem Heap, in einem Array oder auf dem Stack 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 delete-Anweisung geben, die normalerweise von den meisten Entwicklern verwendet wird, um den Heap aufzuräumen. Dies setzt auch voraus, dass kein Wartungsprogrammierer in der Zukunft ein fälschlicherweise wahrgenommenes Speicherleck beheben wird, indem er eine Löschanweisung hinzufügt.

Selbst wenn Sie im Voraus wissen, dass Ihr derzeitiger Plan darin besteht, nur eine einzige Instanz auf dem Heap zuzuweisen, was ist, wenn in Zukunft ein fröhlicher Entwickler vorbeikommt und beschließt, eine Instanz auf dem Stapel 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 “delete this” erreicht, wird er losgehen und ihn löschen, aber wenn das Objekt dann den Gültigkeitsbereich verlässt, ruft er den Destruktor auf. Der Destruktor wird dann versuchen, es erneut zu löschen, und dann werden Sie abgespritzt. In der Vergangenheit hätte so etwas nicht nur das Programm vermasselt, sondern auch das Betriebssystem und der Computer müsste neu gestartet werden. In jedem Fall wird dies dringend NICHT empfohlen und sollte fast immer vermieden werden. Ich müsste verzweifelt, ernsthaft verputzt sein oder die Firma, für die ich gearbeitet habe, wirklich hassen, um Code zu schreiben, der dies tut.

Es ist erlaubt (verwenden Sie das Objekt danach einfach nicht), aber ich würde solchen Code nicht zum Üben schreiben. ich denke, dass delete this sollte nur in aufgerufenen Funktionen erscheinen release oder Release und sieht aus wie: void release() { ref--; if (ref<1) delete this; }.

  • Was genau einmal in jedem Projekt von mir vorkommt… 🙂

    – Cmaster – Wiedereinsetzung von Monica

    9. Juni 2015 um 20:53 Uhr

1647137418 466 Ist delete this in C erlaubt
UnbekanntGosu

Nun, im Komponentenobjektmodell (COM) delete this Konstruktion kann ein Teil davon sein Release Methode, die aufgerufen wird, wenn Sie ein erworbenes Objekt freigeben möchten:

void IMyInterface::Release()
{
    --instanceCount;
    if(instanceCount == 0)
        delete this;
}

  • Was genau einmal in jedem Projekt von mir vorkommt… 🙂

    – Cmaster – Wiedereinsetzung von Monica

    9. Juni 2015 um 20:53 Uhr

1647137418 661 Ist delete this in C erlaubt
Zack Yezek

Dies ist die Kernsprache für referenzgezählte Objekte.

Das Zählen von Referenzen ist eine starke Form der deterministischen Garbage Collection – 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 (die Referenzzählung) im tatsächlichen Objekt erhöhen und verringern.

Wenn die letzte Referenz aus dem Stapel fällt oder gelöscht wird, geht der Referenzzähler auf Null zurück. Das Standardverhalten Ihres Objekts ist dann ein Aufruf von „delete this“ an die Garbage Collection – die Bibliotheken, die ich schreibe, stellen einen geschützten virtuellen „CountIsZero“-Aufruf in der Basisklasse bereit, sodass Sie dieses Verhalten für Dinge wie das Caching außer Kraft setzen können.

Der Schlüssel, um dies sicher zu machen, besteht darin, Benutzern keinen Zugriff auf den CONSTRUCTOR des betreffenden Objekts zu gewähren (es geschützt zu machen), sondern sie stattdessen dazu zu bringen, ein statisches Mitglied – die FACTORY – wie “statische Referenz CreateT (…)” aufzurufen. Auf diese Weise WISSEN Sie sicher, dass sie immer mit gewöhnlichen “neuen” erstellt werden und dass kein roher Zeiger jemals verfügbar ist, sodass “delete this” niemals explodiert.

  • Warum können Sie nicht einfach eine (Singleton-) Klasse “Allocator/Garbage Collector” haben, eine Schnittstelle, über die die gesamte Zuordnung erfolgt, und diese Klasse die gesamte Referenzzählung der zugeordneten Objekte durchführen lassen? Anstatt die Objekte selbst dazu zu zwingen, sich mit Garbage-Collection-Aufgaben zu beschäftigen, etwas, das völlig unabhängig von ihrem vorgesehenen Zweck ist.

    – Ludin

    14. Oktober 2015 um 8:09 Uhr

  • Sie können den Destruktor auch einfach geschützt machen, um statische und Stack-Zuweisungen Ihres Objekts zu verhindern.

    – Spiel_Ouvertüre

    27. Januar 2017 um 15:05 Uhr

995640cookie-checkIst “delete this” in C++ erlaubt?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy