C++ Kurs

Operatoren new und delete

Die Themen:

new Operator

new für einfache Daten und Felder

Der Operator new dient zur Reservierung von Speicher zur Programmlaufzeit und erhält als Operand den Datentyp, für den Speicher reserviert werden soll. Als Ergebnis liefert new einen Zeiger auf den reservierten Speicherbereich zurück. Dieser zurückgelieferte Zeiger besitzt immer den Datentyp *DTYP, wobei DTYP der Datentyp ist, für den der Speicher reserviert wurde.

PgmHeader
// main() Funktion
int main()
{
   // Speicher für ein long Datum reservieren
   auto ptr = new long;
   ...
}

Konnte new aus irgendwelchen Gründen nicht den angeforderten Speicher reservieren, so wird eine Ausnahme (Exception) vom Typ bad_alloc ausgelöst und das Programm (bis jetzt) beendet. Was Ausnahmen sind und wie auf eine solche Ausnahme reagiert werden kann, das folgt später noch.

new kann aber nicht nur Speicher für 'einfache' Daten reservieren, sondern auch für Felder. Soll Speicher für einfache Felder, nicht Objektfelder, reserviert werden, so erfolgt die Angabe der Feldgröße nach dem Datentyp des Feldes innerhalb von eckigen Klammern. Im nachfolgenden Beispiel wird Speicher für ein short-Feld mit 10 Elementen reserviert.

PgmHeader
// main() Funktion
int main()
{
   // Speicher für ein Feld mit 10 short reservieren
   auto ptr = new short[10];
   ...
}

AchtungBeachten Sie, dass bei der Reservierung von Speicher für Felder die Feldgröße in eckigen Klammern stehen muss! Wenn Sie die Feldgröße aus Versehen in runden Klammern angeben, so meldet Ihnen der Compiler keinen Fehler!

// Es sollte Platz für ein short-Feld mit
// 10 Elementen reserviert werden

auto pArray = new short(10);

Vielmehr wird folgender Vorgang ablaufen: Anstelle eines Feldes wird nur Speicher für ein einfaches Datum des entsprechenden Datentyps reserviert. Und dieses Datum wird dann mit dem Wert initialisiert, der innerhalb der runden Klammer angegeben ist. Sie haben damit sozusagen einen Konstruktor für ein einfaches Datum aufgerufen! Im obigen Beispiel wird also Platz für ein short-Datum reserviert und der reservierte Speicher mit dem Wert 10 initialisiert.

new für mehrdimensionale Felder

Soll für mehrdimensionale Felder Speicher reserviert werden, so muss dieses in mehreren Schritten erfolgen. Der 'Trick' dabei besteht darin, dass zuerst ein Zeigerfeld (also ein Feld welches nur Zeiger enthält) reserviert wird. Dieses Zeigerfeld repräsentiert die 'Zeilen' (1. Dimension). Anschließend werden dann in diesem Zeigerfeld die Speicheradressen für die Spaltenfelder (2. Dimension) abgelegt. Sehen wir uns diesen Vorgang nun Schritt für Schritt an:

  1. Zunächst wird ein Zeiger auf einen Zeiger des gewünschten Datentyps des Feldes definiert.
  2. Anschließend wird mittels new ein Zeigerfeld mit so vielen Elementen reserviert, wie das Feld Zeilen haben soll.
  3. Zum Schluss werden in einer Schleife die 'Spaltenfelder' reserviert. Die von new zurückgelieferten Adressen werden dann in dem im 2. Schritt reservierten Zeigerfeld abgelegt.

Der Zugriff auf die einzelnen Elemente des mehrdimensionalen Feldes erfolgt in der üblichen Art und Weise (mehrfache Indizierung).

PgmHeader
// Feld mit 3x2 Elementen float-Zahlen reservieren
const int ROW=3;
const int COLUMN=2;
// Schritt 1: Zeiger auf Zeiger definieren
float **ppArray;

// main() Funktion
int main()
{
   // Schritt 2: Zeigerfeld für 3 float-Zeiger reservieren
   ppArray = new float*[ROW];
   // Schritt 3: Spaltenfelder reservieren
   for (int i=0; i<ROW; i++)
      ppArray[i] = new float[COLUMN];
   ...
   // Zugriff dann wie gewohnt
   ppArray[iRow][iCol] = ...;
}
AchtungAber Achtung! Der für das mehrdimensionale Feld reservierte Speicher muss nicht zwingend zusammenhängend im Speicher liegen.

Damit genug zur Reservierung von Speicher. Sehen wir uns jetzt an, wie der reservierten Speicher wieder freigegeben wird, wenn er nicht mehr benötigt wird.

delete Operator

delete für einfache Daten und Felder

Die Freigabe des mittels new reservierten Speichers erfolgt mit dem delete Operator. Wurde Speicher für 'einfache' Daten (keine Felder) reserviert, so erhält delete als Operanden den von new zurückgelieferten Zeiger. Wurde dagegen Speicher für ein Feld reserviert, so muss nach dem delete Operator zunächst eine leere eckige Klammer stehen und erst danach der von new zurückgelieferte Zeiger. Wird diese leere eckige Klammer vergessen, so meldet der Compiler keinen Fehler! Bei Feldern mit einfachen Datentypen, d.h. keine Objekte, wird der reservierte Speicher auch korrekt freigegeben. Warum bei der Freigabe des Speichers von Objektfeldern diese eckigen Klammern so wichtig sind, das sehen wir uns im nächsten Kapitel über dynamische Objekte an. Vergessen Sie daher niemals die eckigen Klammern bei delete von Feldern.

PgmHeader
int main()
{
   // Speicher für einen long reservieren und wieder freigeben
   auto pData = new long;
   ...
   delete pData;
   // Speicher für char-Feld reservieren und wieder freigeben
   auto pArray = new char[30];
   ...
   delete [] pArray;
}

delete für mehrdimensionale Felder

Sehen wir uns noch kurz an, wie der vorhin reservierte Speicher für ein 2-dimensionales Feld wieder freizugeben ist. Bei der Freigabe des Speichers muss quasi die Reihenfolge der Reservierungen umdreht werden, d.h. es muss zuerst den Speicher für die Spaltenfelder freigegeben werden und dann zum Schluss den Speicher für das Zeigerfeld. Beachten Sie beim delete Operator die leeren eckigen Klammern. Wir haben es hier stets mit Feldern zu tun!

PgmHeader
// Feld mit 3x2 Elementen für die Aufnahme von float-Zahlen reservieren
const int ROW=3;
const int COLUMN=2;
// Schritt 1: Zeiger auf Zeiger definieren
float **ppArray;

// main() Funktion
int main()
{
   // Reservierung des 2-dimimensionalen Feldes wie oben dargestellt
   ...
   // Zuerst Spaltenfelder freigeben
   for (int i=0; i<ROW; i++)
      delete [] ppArray[i];
   // Und Zeigerfeld freigeben
   delete [] ppArray;
}
Achtungdelete gibt nur den Speicher frei, verändert aber den Inhalt des Zeigers nicht, d.h. der Zeiger enthält weiterhin die bisherige Adresse. Sie dürfen aber nach der Ausführung von delete keine weiteren Zugriffe mehr über diesen Zeiger auf den freigegebenen Speicherbereich durchführen.

Und noch ein Hinweis:

HinweisDer C++ Standard erlaubt es, den delete Operator auch mit einem nullptr aufzurufen oder mit einem Zeiger, dessen Inhalt 0 ist (aus historischen Gründen). In diesem Fall führt der delete Operator einfach gar nichts aus.

Beispiel und ÜbungUnd hier geht's zum Beispiel und zur Übung.