Wie funktioniert eigentlich delete genau
-
Hallo Forum,
Hab mal wieder das Bedürfnis, mit Theorie draufzupacken

Diesmal geht es um den Operator delete. Genau beschäftigen
mich zwei Fragen, nämlich1. Wie genau es realisiert ist, dass delete in jeden Objekt(Zeiger?)
bereits eingebaut ist, sind new und delete sone Art versteckter
Operatoren in der Klasse, ähnlich dem this-Zeiger in Elementfunktionen ?
Wenn ja, gibt es noch mehr davon ?2. Woran erkennt delete eigentlich, wie groß der freizugebende Speicher-
bereich genau ist, wird das mit ner Art sizeof() auf den Zeigertyp
realisiert ? Oder reserviert new zwischen den einzelnen Speicherbereichen Lücken, welche die Grenze anzeigen ?
Auf einen void * Zeiger funktioniert delete jedenfalls nicht. Wie sieht es aus,
wenn ich einen Zeiger auf die Basisklasse auf ein abgeleiteten Typ Objekt zeigen lasse und dann delete aufrufe ?Wär schon, wenn mich jemand erleuchtet, FAQ gab leider nichts dazu her.
Danke schonmal,Biff
-
Hi Biff Malibu!
Du solltest dir am besten mal den Quellcode von C++ anschauen, dann weißt du nämlich wie alles realisiert ist!
1. Wie genau es realisiert ist, dass delete in jeden Objekt(Zeiger?)
bereits eingebaut ist, sind new und delete sone Art versteckter
Operatoren in der Klasse, ähnlich dem this-Zeiger in Elementfunktionen ?
Wenn ja, gibt es noch mehr davon ?Ich denke mal delete ist nicht in den Objekten integriert, denn es kann ja sein, dass Zeiger nicht Speicher auf dem Heap reserviert, sondern auf ein anderes Objekt zeigt. In C ist free eine Funktion, in C++ aber halt ein Operator, der nur kompatibel zu Konstruktoren und Desturkoren ist.
2. Woran erkennt delete eigentlich, wie groß der freizugebende Speicher-
bereich genau ist, wird das mit ner Art sizeof() auf den Zeigertyp
realisiert ? Oder reserviert new zwischen den einzelnen Speicherbereichen Lücken, welche die Grenze anzeigen ?
Auf einen void * Zeiger funktioniert delete jedenfalls nicht. Wie sieht es aus,
wenn ich einen Zeiger auf die Basisklasse auf ein abgeleiteten Typ Objekt zeigen lasse und dann delete aufrufe ?Der freizugebende Speicher wird an, dem Typ des Objekts und den Speicher auf dem der Zeiger, gemessen. Diese Informationen genügen delete um Speicher freizugeben. Typ sagt wie groß und Zeiger wo.
Den Rest ist auch nicht schwer zu verstehen void-Zeiger sind an keinem Typ gebunden aber wenn sie gecastet werden sind es auch Zeiger von einem anderen Typ. Und deine letzt Frage erklärt sich so: Der Typ des Zeigers ist von der Basisklasse, und die abgeleiteten Klassen sind ja vom Typ der Basisklasse.
-
wennnich mich richtig an den Meyers erinnere geht das in etwa wie folgt:
zuerst einmal gibt es einmal den Operator "new" und einmal den Operator "operator new", ähnliches gilt für "delete" und "operator delete".
"new" und "delete" gehören zu den Operatoren, die nicht überladen werden können (wie auch die Operatoren ".", ".*", "::", "?:", "sizeof", "typeid" und die 4 cast-Operatoren)Der Operator "new" ist der, der im Quellcode explizit aufgerufen wird:
new Obj(bla);Er sorgt erst für genügend Speicher und initialisiert dann das Objekt in dem Speicher.
Wie schon gesagt, das kann man nicht ändern, da "new" nicht überladen werden kann.
Aber wie er den Speicher besorgt, kann man ändern: er ruft nämlich die Funktion "operator new" auf, die dafür zu sorgen hat, dass sie genügend zusammenhängenden Speicher reserviert und einen Zeiger auf den Anfangsbereich zurückliefert.
Die Deklaration von "operator new" sieh einfach aus:void* operator new (size_t size);(void* weil der Speicher uninitialisiert ist, das initialisieren übernimmt ja "new" hinterher)
"operator new" wird normalerweise nicht direkt aufgerufen, was will man auch mit rohem Speicher. Man kann schließlich als Programmierer keinen Konstruktor auf rohem, schon alloziierten Speicher aufrufen. das kann nur "new". Der Aufruf von "operator new" ginge im Ernstfall etwa wie folgt:void* p = operator new(10);Die Wirkung ist also ähnlich wie die von malloc();
Umgekehrt ruft "delete" erst den Destruktor des Objektes auf und dann "operator delete" um den Speicherbereich entsprechend wieder zurückzugeben.
Genauso gibts zu den Operatoren "new[]" und "delete[]" ein passendes "operator new[]" und "operator delete[]".
Was bringt sowas? Nun, in Scott Meyer's "more effective C++" wir ein Beispiel gegeben, wo es um eine (relativ kleine) Klasse geht, von der es vermutlich sehr viele Instanzen geben wird und wo die "gerburtenrate" und "sterberate" recht groß sein kann. Bei jedem Aufruf von "new" würde normalerweise das Betriebssystem nach einem Fitzelchen Speicher gefragt, der kurz darauf wieder zurückgegeben wird, und das einige hunderte, tausende mal im Programm. Das kann ne Menge Zeit kosten, also macht man folgendes:
"operator new" holt sich beim ersten Aufruf gleich einen ganzen Batzen Speicher vom OS, sagen wir für 1000 Objekte. In der folgenden Zeit können "operator new" und "operator delete" in diesem Speicherbatzen lustig Plätze besetzen und freigeben, ohne dass jedesmal das OS behelligt werden muss.Kleiner hinkender Vergleich: Wenn ne Gruppe von 100 Leuten ins Kino will, geht besser einer und kauf 100 Karten(Sitzplätze=Speicherplätze) und verteilt die dann nach Bedarf, als dass jeder einzelne der 100 Leute einzeln anstehen muss...
-
Stormrage schrieb:
Ich denke mal delete ist nicht in den Objekten integriert, denn es kann ja sein, dass Zeiger nicht Speicher auf dem Heap reserviert, sondern auf ein anderes Objekt zeigt.
Wenn die Daten auf dem Stack liegen, kann man sie nicht freigeben.
-
.filmor schrieb:
Stormrage schrieb:
Ich denke mal delete ist nicht in den Objekten integriert, denn es kann ja sein, dass Zeiger nicht Speicher auf dem Heap reserviert, sondern auf ein anderes Objekt zeigt.
Wenn die Daten auf dem Stack liegen, kann man sie nicht freigeben.
Deshalb ist ja delete nicht in den Objekten integriert!

-
Biff Malibu schrieb:
Diesmal geht es um den Operator delete.
Erstmal gibt es einen kleinen aber feinen Unterschied zwischen Operator delete und delete Operator. Operator delete ist im Grunde einfach nur eine Funktion, welche Bestandteil vom delete Operator ist. Dieser ruft zudem noch den dtor des Objektes auf.
Biff Malibu schrieb:
1. Wie genau es realisiert ist, dass delete in jeden Objekt(Zeiger?)
bereits eingebaut ist, sind new und delete sone Art versteckter
Operatoren in der Klasse, ähnlich dem this-Zeiger in Elementfunktionen ?
Wenn ja, gibt es noch mehr davon ?Wie andere Operatoren teilweise auch, sind new und delete bereits im Compiler integriert. Allerdings mehr auf globaler Ebene, als auf Klassenebene. Dennoch sind es Operatoren, dh operator new und operator delete kann man überladen, sowohl global, als auch in einer Klasse.
Biff Malibu schrieb:
2. Woran erkennt delete eigentlich, wie groß der freizugebende Speicher-
bereich genau ist, wird das mit ner Art sizeof() auf den Zeigertyp
realisiert ? Oder reserviert new zwischen den einzelnen Speicherbereichen Lücken, welche die Grenze anzeigen ?
Auf einen void * Zeiger funktioniert delete jedenfalls nicht. Wie sieht es aus,
wenn ich einen Zeiger auf die Basisklasse auf ein abgeleiteten Typ Objekt zeigen lasse und dann delete aufrufe ?Wie oben bereits erwähnt, ruft der delete Operator nach dem dtor Operator delete auf. Dort findet dann die Speicherfreigabe statt. Wie das genau realisiert wird, ist der Implementation überlassen. IdR liegt dahinter ein Speichermanager, der sich die Grösse des mit new angeforderten Speichers merkt.
-
Stormrage schrieb:
Du solltest dir am besten mal den Quellcode von C++ anschauen, dann weißt du nämlich wie alles realisiert ist!

-
Dank an Alle für die Antworten
Erstmal gibt es einen kleinen aber feinen Unterschied zwischen Operator delete und delete Operator. Operator delete ist im Grunde einfach nur eine Funktion, welche Bestandteil vom delete Operator ist. Dieser ruft zudem noch den dtor des Objektes auf.
Ok, ich hab das jetzt so verstanden, das die (globalen) Operatoren new und delete
nicht überladen werden können, in den Klassen die Funktionen operator new /
operator delete aber schon. Tut man es nicht, dann liefert der Complier die
Standardfunktionalität von new bzw. delete.Wie oben bereits erwähnt, ruft der delete Operator nach dem dtor Operator delete auf. Dort findet dann die Speicherfreigabe statt. Wie das genau realisiert wird, ist der Implementation überlassen. IdR liegt dahinter ein Speichermanager, der sich die Grösse des mit new angeforderten Speichers merkt.
Und genau das hat mich anfangs stutzig gemacht. Mir war einfach nicht klar, warum Code wie dieser keine Speicherlecks erzeugt:
class class1 { public: class1() {cout << "Konstruktor! " << endl;}; ~class1() {cout << "Destruktor! " << endl;}; private: int a; }; class class2 : public class1 { public: class2() {cout << "Konstruktor2!" << endl;}; ~class2() {cout << "Destruktor2!" << endl;}; private: char data[1000]; }; class1 * wrongtype(void) { class1 * p2; p2 = new class2; return p2; } int main() { char c; class1* p=wrongtype(); delete p; return 0; }Mir war u.A. nicht klar, woher delete weiß, das es Speicher für ein
class2-Objekt freigeben soll, das dahinter ein Memory-Management steckt
wußte ich noch nicht.
Wer braucht da noch managed c++

gruß,
Biff
-
Dieser Code mag zwar bei dir kein Speicherleck erzeugen, hat aber undefiniertes Verhalten. Wenn du ein Objekt über einen Basisklassenzeiger löschen willst, muss der Destruktor virtual sein.
-
Biff Malibu schrieb:
Ok, ich hab das jetzt so verstanden, das die (globalen) Operatoren new und delete
nicht überladen werden können, in den Klassen die Funktionen operator new /
operator delete aber schon. Tut man es nicht, dann liefert der Complier die
Standardfunktionalität von new bzw. delete.Nicht ganz.
OK, nochmal ganz langsam.
delete a;Das Stück Code zeigt den delete Operator in Aktion. Dieser macht dabei zwei Sachen:
1. dtor des Objektes hinter a aufrufen
2. operator delete aufrufen (für Speicherfreigabe)
Den delete Operator kann man nicht überladen, operator delete aber schon, sowohl global als auch lokal. Sollte eine Klasse eine eigene operator delete Funktion definieren, wird diese für entsprechende Objekte verwendet, ansonsten kommt das globale operator delete zum Einsatz.