Selbstzerstörende Klassen
-
Hi,
ich bin alles andere als ein C++ Experte. Trotzdem habe ich bei meiner Arbeit immer wieder etwas damit zu tun.
Für meine Arbeitskollegen musste ich eine Netzwerk-Lib implementieren. Um ihnen die Arbeit zu vereinfachen, habe ich selbstzerstörende C++ Klassen gebaut.Die Klassen mussten auf dem Heap alloziert werden, da sie ja auch innerhalb einer Funktion aufgerufen werden konnten. Und da der Speicher hinterher aufgeräumt werden muss, habe ich die Klasse nach einer Operation einfach selbst zerstört.
Meine Frage ist, ob das ok ist, wenn das auch so dokumentiert ist?
So ähnlich habe das umgesetzt:
#include <memory> #include <iostream> SelfDestructingClass::SelfDestructingClass() = default; SelfDestructingClass::~SelfDestructingClass() { std::cout << "Destructor called!" << std::endl; } void SelfDestructingClass::networkTransmissions() { std::unique_ptr<SelfDestructingClass> self(this); std::cout << "Data transmitted" << std::endl; }
Der Code funktioniert und tut was er soll. Aber habe ich irgendetwas übersehen?
Die Gefahr ist natürlich, dass die Klasse auf dem Stack angelegt wird. Aber da das sowieso per Design falsch ist, da der Code asynchron abläuft, sehe ich da kein Problem, wenn ich den Nutzer dazu zwinge, ein Objekt immer auf dem Heap anzulegen.
-
Welchen Sinn soll denn
std::unique_ptr<SelfDestructingClass> self(this);
machen?
Und wie verwendest du die Klasse?
Schau dir mal RAII an, denn es sollte keinen Unterschied machen, ob ein Objekt einer Klasse auf dem Stack oder auf dem Heap (Freispeicher) angelegt wird.
Edit: Und ob der Code asynchron oder nicht abläuft, sollte erst recht keinen Unterschied bzgl. der Allokation machen!
-
Th69 schrieb:
Und ob der Code asynchron oder nicht abläuft, sollte erst recht keinen Unterschied bzgl. der Allokation machen!
Wenn du eine Netzwerkklasse innerhalb einer Funktion auf dem Stack anlegst und eine nicht-blockierende asynchrone Funktion aufrufst, baut sich der Stack ab noch bevor die Klasse die Daten vollständig übertragen konnte.
Die Klasse wird ungefähr so verwendet:
void transmitData() { QCoreApplication a(argc, argv); SelfDestructingClass* foo = new SelfDestructingClass(); // Nichtblockierender Aufruf. Stack wird danach sofort abgebaut. foo->networkTransmissions(); }
-
Sorry, da war noch überflüssiger Code dabei.
Hier nochmal:void transmitData()
{
SelfDestructingClass* foo = new SelfDestructingClass();
// Nichtblockierender Aufruf. Stack wird danach sofort abgebaut.
foo->networkTransmissions();
}
-
Designmäßig solltest du keine Instanz in der
transmitData()
-Funktion erstellen, sondern sie übergeben.Und irgendwo in deinem Code muss doch wieder ein blockierender Aufruf sein, der beispielsweise wartet, bis die Arbeiten zu Ende sind.
Erstelle deine Instanzen wohl am besten in genau diesem Scope.
-
stateofmind schrieb:
Designmäßig solltest du keine Instanz in der
transmitData()
-Funktion erstellen, sondern sie übergeben.Kann man machen. Dann muss aber Aufrufer das Objekt selbst löschen. Es gibt nämlich einen weiteren Grund, weshalb man eine Heap-Allozierung machen muss: Ich verhindere nämlich, dass die Methode networkTransmissions() mehr als einmal aufgerufen wird. Das könnte nämlich zu Race Conditions führen. D. h. ein Objekt darf nur einmal verwendet werden.
Ein Objekt von SelfDestructingClass kann also keine Member-Variable einer Klasse sein, außer natürlich als Pointer.Und irgendwo in deinem Code muss doch wieder ein blockierender Aufruf sein, der beispielsweise wartet, bis die Arbeiten zu Ende sind.
Nein. Es gibt eine asynchrone Benachrichtigung. Das funktioniert über Signal/Slot über Qt.
-
Schlingel schrieb:
Sorry, da war noch überflüssiger Code dabei.
Hier nochmal:void transmitData()
{
SelfDestructingClass* foo = new SelfDestructingClass();
// Nichtblockierender Aufruf. Stack wird danach sofort abgebaut.
foo->networkTransmissions();
}Schieb transmitData mit nach SelfDestructingClass rein. Dann machst du noch den SelfDestructingClass ctor private. Problem gelöst.
Bzw. stellt sich überhaupt die Frage wozu man die SelfDestructingClass Klasse überhaupt braucht. Also als Implementierungs-Detail in einem anonymen Namespace irgendwo in einem .cpp File ist das OK, klar. Aber ausserhalb tut's vielleicht auch eine freie
transmitData
Funktion.Und sobald die Klasse nur mehr ein Implementierungs-Detail in einem anonymen Namespace ist, kann auch keiner mehr Quatsch damit bauen.
-
Du benutzt doch hoffentlich die hausinternen Bordmittel von Qt.
Dazu gehört dann auch der Networking Teil (QTcpSocket, QNetworkRequest, etc.), da kannst du einen Parent setzen. Wenn dieser Parent freigegeben wird, geschieht das auch bei seinen Kindern.
Wenn du das benutzt geht schlussendlich alles nur noch über den Signal/Slot-Mechanismus, kuck dir hierzu die Referenzen der Klassen an.
-
hustbaer schrieb:
Schieb transmitData mit nach SelfDestructingClass rein. Dann machst du noch den SelfDestructingClass ctor private. Problem gelöst.
Bzw. stellt sich überhaupt die Frage wozu man die SelfDestructingClass Klasse überhaupt braucht. Also als Implementierungs-Detail in einem anonymen Namespace irgendwo in einem .cpp File ist das OK, klar. Aber ausserhalb tut's vielleicht auch eine freie
transmitData
Funktion.Und sobald die Klasse nur mehr ein Implementierungs-Detail in einem anonymen Namespace ist, kann auch keiner mehr Quatsch damit bauen.
Das ändert aber nichts daran, dass SelfDestructingClass auf dem Heap alloziert werden und aufgeräumt werden muss. Die Frage ist, ob mein Ansatz ok ist.
Wozu man SelfDestructingClass braucht? Wie wäre es mit Wiederverwendbarkeit? Wie gesagt, ist das Teil einer Lib von mir und was ich hier gezeigt habe, ist ganz schön abgespeckt.
stateofmind schrieb:
Du benutzt doch hoffentlich die hausinternen Bordmittel von Qt.
Dazu gehört dann auch der Networking Teil (QTcpSocket, QNetworkRequest, etc.), da kannst du einen Parent setzen. Wenn dieser Parent freigegeben wird, geschieht das auch bei seinen Kindern.
Wenn du das benutzt geht schlussendlich alles nur noch über den Signal/Slot-Mechanismus, kuck dir hierzu die Referenzen der Klassen an.
Das nutze ich schon. Trotzdem kann eine Instanz von SelfDestructingClass in einem Objekt leben das langlebig ist. Daher muss die SelfDestructingClass-Instanz früh genug gelöscht werden.
-
Schlingel schrieb:
Das ändert aber nichts daran, dass SelfDestructingClass auf dem Heap alloziert werden und aufgeräumt werden muss. Die Frage ist, ob mein Ansatz ok ist.
Welcher Ansatz?
-
hustbaer schrieb:
Schlingel schrieb:
Das ändert aber nichts daran, dass SelfDestructingClass auf dem Heap alloziert werden und aufgeräumt werden muss. Die Frage ist, ob mein Ansatz ok ist.
Welcher Ansatz?
Dass sich die Klasse selbst zerstört, wenn sie ihre Aufgabe getan hat.
In meiner Lib wird vorher dem Observer eine Nachricht gesendet (über Signal/Slot).
-
"OK" in dem Sinn dass es definiertes Verhalten ist ist "delete this" auf jeden Fall.
Ob es OK ist eine Klasse anzubieten die man nur mit new instanzieren darf, und die Der Benutzer dann ab Moment X nicht mehr zerstören darf, weil sie es dann selbst tut, musst du selbst wissen. Ich sag mal nein, aber das wolltest du ja nicht hören.
-
Schlingel schrieb:
Um ihnen die Arbeit zu vereinfachen, habe ich selbstzerstörende C++ Klassen gebaut.
Der Satz hat was. Könnte man als Off-Kommentar bei einem SciFi Trash-Film verwenden.