GC und delete
-
Hallo,
man nehme den folgenden Code:
ref class A { }; int main(array<System::String ^> ^args) { A ^m = gcnew A(); A ^n = m; delete n; delete m; return 0; }
delete m; wird anstandslos durchgeführt, obgleich das Objekt auf dem CRT-Heap durch delete n; bereits zerstört sein müsste. Kann es sein, dass der Garbage Collector trotz dem expliziten delete das Objekt erst zu einem späteren Zeitpunkt zerstört oder erst dann zerstört wenn kein Handle mehr darauf zeigt?
Gruss
foo
-
Ein zweifaches delete führt zu undefiniertem Verhalten. Da darf alles passieren.
-
Knuddlbaer schrieb:
Da darf alles passieren.
Ich hätte auch irgend einen Abbruch erwartet, denn in einen catch(...) springt er damit auch nicht.
Entweder wird das Objekt beim nächsten GC-Zyklus entfernt, sodass es wg. dem unmittelbaren 2. delete noch nicht dazu gekommen ist oder das Objekt wird erst dann entfernt, wenn kein Handle mehr darauf zeigt, sodass der erste delete nichts bewirkte.
Das kann man auch noch weiter treiben:ref class A { public: int v; ~A(){ Console::WriteLine(__FUNCSIG__); } }; int main(array<System::String ^> ^args) { A ^m = gcnew A(); A ^n = m; delete n; m->v = 100; delete m; return 0; }
Das Zuweisen der Membervariablen v funktioniert, ebenso wird der Destruktor 2x aufgerufen. Schon merkwürdig.
Gruss
foo
-
Knuddlbaer schrieb:
Ein zweifaches delete führt zu undefiniertem Verhalten. Da darf alles passieren.
@foodax:
Was verstehst Du nicht an "alles"?Es könnte nichts passieren, es könnte ein Abbruch erfolgen, es könnte ein Sack Reis umfallen, Deine Platte könnte gelöscht werden, ...
-
LordJaxom, die Geschichte mit dem Sack Reis bezweifle ich!
In der Debug-Version hätte ich einen Abbruch oder Assertion (wie unter VC 6 mit Pointern) erwartet.
Manche Dinge muss man einfach so nehmen wie sie sind.Gruss
foo
-
Bei C++/CLI ist der Aufruf des Destruktors (wie dus halt mit delete machst) nicht mit dem abbau des Objektes gleichzusetzen!
Dadurch ist es möglich, wie du schon gesehen hast, das Ein Destruktor (der intern in die Funktion "Dispose" umgewandelt wird^^) mehrmals aufgerufen werden kann.
Freigegeben wird das Objekt erst sobald kein Trackinghandle mehr darauf zeigt
Aufpassen musst du dann, wenn du noch nicht verwaltete Objekt verwendest. Für die musst du dann zum einen im Destruktor eine Abfrage erstellen, du prüft ob der Destruktor bereits aufgerufen wurde, und zum andern eine Methode schreiben (Stichwort: Finalizer) die die Objekte wieder Freigibt (der GC kann mit den nichts anfangen also musst du ihm sagen was mit dennen passsiert)zb
ref class A { std::string* str; bool disposed; public: A(const std::string& s) :disposed(false) { str = new std::string(s); } ~A() { if(!disposed) { this->!A(); // this ist zwingend // Teilt GC mit das der Finalizer // nichtmehr aufgerufen werden soll System::GC::SuppressFinalize(this); disposed = true; } } // Finalizer // ! gefolgt vom Klassennamen !A() { delete(str); } };
-
Ein "delete" auf eine GC-Ref ruft nicht den destruktor sondern Dispose auf.. somit kann man mehrmaks delete aufrufen.
-
Jochen Kalmbach schrieb:
Ein "delete" auf eine GC-Ref ruft nicht den destruktor sondern Dispose auf.. somit kann man mehrmaks delete aufrufen.
Nichts gegen nen Mod aber ich glaub zumindest dieso sollten sich die Posts genau durchlesen und merken das über ihrem Post genau das gleiche steht
-
Lies Dich da mal rein, bitte...
Das ist ein Projekt aus meiner Schule in Gera./*################################################################# Destruktor und Finalisierer ##################################################################*/ #include "stdafx.h" using namespace System; //die Klasse Trabant ref class trabant { Int32 alter; Int32 kilometer; public: //der Konstruktor trabant(Int32, Int32); //der Destruktor ~trabant(); //der Finalisierer !trabant(); void ansehen(); }; //die Methode für den Konstruktor trabant::trabant(Int32 tAlter, Int32 tKilometer) { alter = tAlter; kilometer = tKilometer; } //die Methode für den Destruktor trabant::~trabant() { //bitte in einer Zeile eingeben Console::WriteLine("Der Destruktor für den {0} Jahre alten Trabant wurde aufgerufen.\n",alter); } //die Methode für den Finalisierer trabant::!trabant() { //bitte in einer Zeile eingeben Console::WriteLine("Der Finalisierer für den {0} Jahre alten Trabant wurde aufgerufen.\n",alter); } void trabant::ansehen() { //bitte jeweils in einer Zeile eingeben Console::WriteLine("Der Trabant ist {0} Jahr(e) alt.\n",alter); Console::WriteLine("Die Trabant hat eine Laufleistung von {0} Kilometer.\n",kilometer); } //eine Funktion mit einer lokalen Instanz, die nicht auf dem verwalteten Heap liegt void lokaleInstanz() { trabant trabant4(12,28000); trabant4.ansehen(); } int main(array<System::String ^> ^args) { //Instanzen auf dem verwalteten Heap erzeugen trabant^ trabant1 = gcnew trabant(4,2500); trabant^ trabant2 = gcnew trabant(5,3000); trabant^ trabant3 = gcnew trabant(6,4500); //eine lokale Instanz erzeugen lokaleInstanz(); //Attribute anzeigen trabant3->ansehen(); //Instanz trabant1 per Hand "zerstören" delete trabant1; trabant1->ansehen(); return 0; }
Grüße aus Leipzig vom Klaus.
-
Klaus1982 schrieb:
Lies Dich da mal rein, bitte...
Meine Vermutung bzgl. des Verhaltens der ref-Objekte wurde bereits zweifach bestätigt.
Dies hier ist sicherlich nur eine der vielen Stellen wo der "normale" C++-Programmierer umdenken muss bzw. die unterschiedlichen Verhaltensweisen im Vergleich zu ANSI C++ kennen sollte. Alles eine Frage der Zeit/Erfahrung/Training.
Danke an alle beteiligten für die Aufklärung!Gruss
foo