====== Dynamisch erzeugte Variablen: new und delete ====== Mit dem Operator **new** kann man Variablen während der Laufzeit des Programms erzeugen. Dieser Operator versucht, in einem eigens dafür vorgesehenen Speicherbereich (der oft auch als **Heap, dynamischer Speicher** oder **freier Speicher** bezeichnet wird) so viele Bytes zu reservieren, wie eine Variable des angegebenen Datentyps benötigt. Falls der angeforderte Speicher zur Verfügung gestellt werden konnte, liefert ''new'' seine Adresse zurück. Andernfalls wird eine **Exception** des Typs ''std::bad_alloc'' ausgelöst, die einen Programmabbruch zur Folge hat, wenn sie nicht mit einer ''try''- Anweisung abgefangen wird. Da unter 32-bit-Systemen wie Windows unabhängig vom physisch vorhandenen Hauptspeicher 2 GB virtueller Speicher zur Verfügung stehen, kann man meist davon ausgehen, dass der angeforderte Speicher verfügbar ist. Variablen, die zur Laufzeit erzeugt werden, bezeichnet man auch als **dynamisch erzeugte Variablen**. Wenn der Unterschied zu vom Compiler erzeugten Variablen (wie „''int i;''“) betont werden soll, werden diese als „gewöhnliche“ Variablen bezeichnet. Die folgenden Beispiele zeigen, wie man ''new'' verwenden kann: 1. Für einen Datentyp T reserviert „''new T''“ so viele Bytes auf dem Heap, wie für eine Variable des Datentyps T notwendig sind. Der Ausdruck „''new T''“ hat den Datentyp „Zeiger auf T“. Sein Wert ist die Adresse des reservierten Speicherbereichs und kann einer Variablen des Typs „Zeiger auf T“ zugewiesen werden: int* pi; pi=new int; //reserviert sizeof(int) Bytes und weist pi die Adresse dieses Speicherbereichs zu *pi=17; // initialisiert den reservierten Speicherbereich Am besten initialisiert man eine Zeigervariable immer gleich bei ihrer Definition: int* pi=new int; // initialisiert pi mit der Adresse *pi=17; Die explizit geklammerte Version von //new// kann zur Vermeidung von Mehrdeutigkeiten verwendet werden. Für einfache Datentypen sind beide Versionen gleichwertig: pi=new(int); // gleichwertig zu pi=new int; 2. In einem //new-expression// kann man nach dem Datentyp einen //new-initializer// angeben: Er bewirkt die Initialisierung der mit new erzeugten Variablen. Die zulässigen Ausdrücke und ihre Bedeutung hängen vom Datentyp der dynamisch erzeugten Variablen ab. Für einen fundamentalen Datentyp (wie //int, double// usw.) gibt es drei Formen: a) Der in Klammern angegebene Wert wird zur Initialisierung verwendet: double* pd=new double(1.5); //Initialisierung *pd=1.5 b) Gibt man keinen Wert zwischen den Klammen an, wird die Variable mit 0 (''NULL'') initialisiert: double* pd=new double(); //Initialisierung *pd=0 c) Ohne einen Initialisierer ist ihr Wert unbestimmt: double* pd=new double;// *pd wird nicht initialisiert Für einen Klassentyp müssen die Ausdrücke Argumente für einen Konstruktor sein. 3. Gibt man nach einem Datentyp T in eckigen Klammern einen ganzzahligen Ausdruck >= 0 an, wird ein **Array dynamisch erzeugt** reserviert. Die Anzahl der Arrayelemente ist durch den ganzzahligen Ausdruck gegeben. Im Unterschied zu einem gewöhnlichen Array muss diese Zahl keine Konstante sein: typedef double T; // T irgendein Datentyp, hier double int n=100; // nicht notwendig eine Konstante T* p=new T[n]; // reserviert n*sizeof(T) Bytes Wenn durch einen //new//-Ausdruck ein Array dynamisch erzeugt wird, ist der Wert des Ausdrucks die Adresse des ersten Arrayelements. Die einzelnen Elemente des Arrays können folgendermaßen angesprochen werden: p[0], ..., p[n–1] // n Elemente des Datentyps T Die Elemente eines dynamisch erzeugten Arrays können nicht bei ihrer Definition initialisiert werden. 4. Mit dem nur selten eingesetzten //new-placement// kann man eine Variable an eine bestimmte Adresse platzieren. Damit wird keine Variable wie bei einem gewöhnlichen //new//-Ausdruck auf dem Heap angelegt. Die Adresse wird als Zeiger nach new angegeben und ist z.B. die Adresse eines zuvor reservierten Speicherbereichs: int* pi=new int; double* pd=new(pi) double;// erfordert #include Durch die letzte Anweisung kann man den Speicherbereich ab der Adresse von ''i'' als //double// ansprechen wie nach double* pd=(double*)pi; // explizite Typkonversion Dieses Beispiel zeigt, dass die meisten Programme keine Anwendungen für ein //new placement// haben. Nützlichere Anwendungen gibt es bei Betriebssystemen, die (anders als Windows) Geräte über physikalische Adressen ansprechen. 5. Variable, deren Datentyp eine Klasse der VCL ist, müssen mit new angelegt werden. Dabei muss dem Konstruktor der Eigentümer (z.B. //Form1//) übergeben werden. TEdit* pe = new TEdit(Form1); pe->Parent = Form1; pe ->SetBounds(10, 20, 100, 30); pe ->Text = "blablabla"; Es ist nicht möglich, VCL-Komponenten vom Compiler erzeugen zu lassen: TEdit e; bzw. TEdit e(Form1); // Fehler Eine gewöhnliche Variable existiert von ihrer Definition bis zum Ende des Bereichs (bei einer lokalen Variablen ist das der Block), in dem sie definiert wurde. Im Unterschied dazu existiert eine dynamisch erzeugte Variable bis der für sie reservierte Speicher mit dem Operator delete wieder freigegeben oder das Programm beendet wird. Man sagt auch, dass eine dynamisch erzeugte Variable durch den Aufruf von //delete// **zerstört** wird. Die erste dieser beiden Alternativen ist für Variable, die keine Arrays sind, und die zweite für Arrays. Dabei muss //cast-expression// ein Zeiger sein, dessen Wert das Ergebnis eines //new//-Ausdrucks ist. Nach //delete p// ist der Wert von //p// unbestimmt und der Zugriff auf //*p// unzulässig. Falls //p// den Wert 0 hat, ist //delete p// wirkungslos. Damit man mit dem verfügbaren Speicher sparsam umgeht, sollte man den Operator delete immer dann aufrufen, wenn eine mit new erzeugte Variable nicht mehr benötigt wird. Unnötig reservierter Speicher wird auch als **Speicherleck (memory leak)** bezeichnet. Ganz generell sollte man diese **Regel** beachten: Jede mit **//new//** erzeugte Variable sollte mit **//delete//** auch wieder freigegeben werden. Die folgenden Beispiele zeigen, wie die in den letzten Beispielen reservierten Speicherbereiche wieder freigegeben werden. 1. Der Speicher für die unter 1. und 2. erzeugten Variablen wird folgendermaßen wieder freigegeben: delete pi; delete pd; 2. Der Speicher für das unter 3. erzeugte Array wird freigegeben durch delete[] p; 3. Es ist möglich, die falsche Form von //delete// zu verwenden, ohne dass der Compiler eine Warnung oder Fehlermeldung ausgibt: delete[] pi; // Arrayform für Nicht-Array delete p; // Nicht-Arrayform für Array Im C++-Standard ist explizit festgelegt, dass das Verhalten nach einem solchen falschen Aufruf undefiniert ist. Oben wurde empfohlen, einen Zeiger, der nicht auf reservierten Speicher zeigt, immer auf 0 (Null) zu setzen. Deshalb sollte man einen Zeiger nach //delete// immer auf 0 setzen, z.B. nach Beispiel 1: pi=0; pd=0; Stroustrup empfiehlt dafür eine Funktion wie **//destroy//**: void destroy(int*& p) {//http://www.research.att.com/~bs/bs_faq2.html delete p; // delete[] für Zeiger auf dynamische Arrays p = 0; } In dieser Function wird der Zeiger-Parameter als Referenz übergeben, da sich die Zuweisung von 0 auf das Argument auswirken soll. Die wichtigsten Unterschiede zwischen dynamisch erzeugten und „gewöhnlichen“ (vom Compiler erzeugten) Variablen sind: 1. Eine dynamisch erzeugte Variable hat im Unterschied zu einer gewöhnlichen Variablen **keinen Namen** und kann nur **indirekt** über einen Zeiger angesprochen werden. Nach einem erfolgreichen Aufruf von p=new type enthält der Zeiger p die Adresse der Variablen. Falls p überschrieben und nicht anderweitig gespeichert wird, gibt es keine Möglichkeit mehr, sie anzusprechen, obwohl sie weiterhin existiert und Speicher belegt. Der für sie reservierte Speicher wird erst beim Ende des Programms wieder freigegeben. Da zum Begriff “Variable” auch ihr Name gehört, ist eine “namenlose Variable” eigentlich widersprüchlich. Im C++-Standard wird „object“ als Oberbegriff für namenlose und benannte Variablen verwendet. Ein **Objekt** in diesem Sinn hat wie eine Variable einen Wert, eine Adresse und einen Datentyp, aber keinen Namen. Da der Begriff „Objekt“ aber auch oft für Variable eines Klassentyps (siehe [[Objekte]]) verwendet wird, wird zur Vermeidung von Verwechslungen auch der Begriff „namenlose Variable“ verwendet. 2. Der Name einer gewöhnlichen Variablen ist untrennbar mit reserviertem Speicher verbunden. Es ist nicht möglich, über einen solchen Namen nicht reservierten Speicher anzusprechen. Im Unterschied dazu existiert ein Zeiger p, über den eine dynamisch erzeugte Variable angesprochen wird, unabhängig von dieser Variablen. Ein Zugriff auf *p ist nur nach new und vor delete zulässig. Vor //new p// oder nach //delete p// ist der Wert von //p// unbestimmt und der Zugriff auf //*p// ein Fehler, der einen Programmabbruch zur Folge haben kann: int* pi = new int(17); delete pi; ... *pi=18; // Jetzt knallts - oder vielleicht auch nicht? 3. Der Speicher für eine gewöhnliche Variable wird **automatisch** freigegeben, wenn der Gültigkeitsbereich der Variablen verlassen wird. Der Speicher für eine dynamisch erzeugte Variable **muss** dagegen mit genau einem Aufruf von **//delete//** wieder freigegeben werden. – Falls //delete// überhaupt nicht aufgerufen wird, kann das eine Verschwendung von Speicher (Speicherleck, memory leak) sein. Falls in einer Schleife immer wieder Speicher reserviert und nicht mehr freigegeben wird, können die swap files immer größer werden und die Leistungsfähigkeit des Systems nachlassen. – Ein zweifacher Aufruf von //delete// mit demselben, von Null verschiedenen Zeiger, ist ein Fehler, der einen Programmabsturz zur Folge haben kann. Beispiel: Anweisungen wie int* pi = new int(1); int* pj = new int(2); zur Reservierung und delete pi; delete pj; zur Freigabe von Speicher sehen harmlos aus. Wenn dazwischen aber eine ebenso harmlos aussehende Zuweisung stattfindet, pi = pj; haben die beiden //delete// Anweisungen den Wert von //pj// als Operanden. Diese Zuweisung führt also dazu, dass //pj// doppelt und //pi// überhaupt nicht freigegeben wird. 4. Oben haben wir gesehen, dass eine Zuweisung von Zeigern zu Aliasing führen kann. Bei einer Zuweisung an einen Zeiger auf eine dynamisch erzeugte Variable besteht außerdem noch die Gefahr von Speicherlecks. Das kann z.B. mit den folgenden Strategien vermieden werden: – Man vermeidet solche Zuweisungen. Diese Strategie wird von der //smart pointer// Klasse //scoped_ptr// der Boost-Bibliothek (siehe http://boost.org/) verfolgt. – Der Speicher für diese Variable wird vorher freigegeben. int* pi = new int(17); int* pj = new int(18); delete pi; // um ein Speicherleck zu vermeiden pi = pj; Da anschließend zwei Zeiger pi und pj auf die mit new(18) erzeugte Variable *pj zeigen, muss darauf geachtet werden, dass der Speicher für diese Variable nicht freigegeben wird, solange über einen anderen Zeiger noch darauf zugegriffen werden kann. Diese Strategie wird von der smart pointer Klasse shared_ptr verfolgt. – Der Speicher für eine dynamisch erzeugte Variable wird automatisch wieder freigegeben, wenn es keine Referenz mehr auf diese Variable gibt. Das wird als **garbage collection** bezeichnet. Garbage collection gehört allerdings noch nicht zum C++-Standard 2003. Es steht aber über die //smart pointer// Klasse //shared_ptr// der Boost-Bibliothek (siehe Abschnitt 3.12.5) sowie in speziellen Erweiterungen wie z.B. C++/CLI zur Verfügung. Es soll außerdem in den nächsten C++-Standard aufgenommen werden. 5. Der Operand von //delete// muss einen Wert haben, der das Ergebnis eines //new//-Ausdrucks ist. Wendet man //delete// auf einen anderen Ausdruck an, ist das außer bei einem Nullzeiger ein Fehler, der einen Programmabbruch zur Folge haben kann. Insbesondere ist es ein Fehler, //delete// auf einen Zeiger anzuwenden, a) dem nie ein //new//-Ausdruck zugewiesen wurde.\\ b) der nach //new// verändert wurde.\\ c) der auf eine gewöhnliche Variable zeigt. Beispiele: Der Aufruf von //delete// mit //p1, p2// und //p//3 ist ein Fehler: int* p1; // a) int* p2 = new int(1); p2++; // b) int i = 17; int* p3 = &i; // c) 6. Bei gewöhnlichen Variablen prüft der Compiler bei den meisten Operationen anhand des Datentyps, ob sie zulässig sind oder nicht. Ob für einen Zeiger //delete// aufgerufen werden muss oder nicht aufgerufen werden darf, ergibt sich dagegen nur aus dem bisherigen Ablauf des Programms. Beispiel: Nach Anweisungen wie den folgenden ist es unmöglich, zu entscheiden, ob //delete p// aufgerufen werden muss oder nicht: int i,x; int* p; if (x>0) p=&i; else p = new int; Diese Beispiele zeigen, dass mit dynamisch erzeugten Variablen **Fehler** möglich sind, die mit „gewöhnlichen“ Variablen nicht vorkommen können. Zwar sehen die Anforderungen bei den einfachen Beispielen hier gar nicht so schwierig aus. Falls aber new und //delete// in verschiedenen Teilen des Quelltextes stehen und man nicht genau weiß, welche Anweisungen dazwischen ausgeführt werden, können sich leicht Fehler einschleichen, die nicht leicht zu finden sind. – Deshalb sollte man **„gewöhnliche“ Variablen** möglichst immer **vorziehen**.\\ – Falls sich Zeiger nicht vermeiden lassen, sollte man das Programm immer so **einfach** gestalten, dass möglichst keine Unklarheiten aufkommen können.\\ – **Smart pointer** sind oft eine Alternative, die viele Probleme vermeidet. Dynamisch erzeugte Variable bieten in der bisher verwendeten Form keine Vorteile gegenüber „gewöhnlichen“ Variablen. Trotzdem gibt es Situationen, in denen sie notwendig sind: * Falls gewisse Informationen erst zur Laufzeit und nicht schon bei der Kompilation verfügbar sind. So muss zum Beispiel\\ * bei dynamisch erzeugten Arrays die Größe,\\ * bei verketteten Datenstrukturen (wie z.B. Listen und Bäume) die Verkettung, und \\ * bei einem Zeiger auf ein Objekt einer Basisklasse der Datentyp des Objekts, auf den er zeigt, erst während der Laufzeit festgelegtwerden. Aus diesem Grund werden die Elemente von objektorientierten Klassenhierarchien oft dynamisch erzeugt.\\ * Es gibt Datentypen wie z.B. die Klassen der VCL, mit denen man keine gewöhnlichen Variablen anlegen kann. Wenn man z.B. eine Variable des Typs //TEdi//t will, muss man sie dynamisch mit //new// erzeugen.\\ * Es gibt Betriebssysteme und Compiler, bei denen der für globale und lokale Variablen verfügbare Speicher begrenzt ist (z.B. auf 64 KB bei 16-bit-Systemen wie Windows 3.x oder MS-DOS). Für den Heap steht dagegen mehr Speicher zur Verfügung. Unter 32-bit-Betriebssystemen stehen für globale und lokale Variablen unabhängig vom physikalisch verfügbaren Hauptspeicher meist 2 GB oder mehr zur Verfügung. Deshalb besteht heutzutage meist keine Veranlassung, Variablen aus Platzgründen im Heap anzulegen. Allerdings findet man noch Relikte aus alten Zeiten, in denen Variablen nur aus diesem Grund im Heap angelegt werden.\\ * Sie können zur Reduzierung von Abhängigkeiten bei der Kompilation beitragen (Sutter 2000, Items 26-30). Hinweis: In der Programmiersprache C gibt es anstelle der Operatoren new und delete die Funktionen malloc bzw. free. Sie funktionieren im Prinzip genauso wie new und delete und können auch in C++ verwendet werden. Da sie aber mit void*-Zeigern arbeiten, sind sie fehleranfälliger. Deshalb sollte man immer new und delete gegenüber malloc bzw. free bevorzugen. Man kann in einem Programm sowohl malloc, new, free und delete verwenden. Speicher, der mit new reserviert wurde, sollte aber nie mit free freigeben werden. Dasselbe gilt auch für malloc und delete.