Wie wir gesehen haben, muss ein Array in C++ immer eine
feste, also statische, Größe besitzen. Wenn wir
diese Limitierung
umgehen möchten, haben wir zwei Möglichkeiten.
- Wir verwalten einen Array in beliebiger
Größe von Hand
- Wir verwenden die Standard Template Library
Zur zweiten Lösung kommen wir später.
Ein dynamisches Array
Sehen wir uns an, wie wir ein Array dynamischer, also
beliebiger,
Größe
von Hand erzeugen und löschen können:
void
dynamicArray(unsigned int size)
{
// Laufvariable deklarieren
unsigned int i;
// Array im Speicher anlegen
int* a = new
int[size];
// Array initialisieren
for (i = 0; i
< size; i++)
{
a[i] = int(i);
}
for (i = 0; i
< size; i++)
{
//
Werte von a ausgeben
if
(i+1 < size)
cout << a[i] << " | ";
else
cout << a[i] << endl;
}
// Array löschen
(die eckigen
Klammern beachten)
delete[] a;
}
Wir deklarieren zuerst einen Zeiger (bzw. ein leeres
Array) a. Mit Hilfe des Befehls new
int[size] erzeugen wir ein Integer-Array
der Größe size und weisen
es a zu. Am Ende der Funktion
löschen wir unser Array mit dem delete[]
Befehl. Zwischen diesen beiden Befehlen können wir wie gewohnt
auf unser Array zugreifen.
Dynamische Objekte
Zu etwas einfacherem. new
und delete
können auch verwendet werden, um einzelne Objekte zu erzeugen
und wieder zu löschen. Um zu sehen, was da passiert, verwenden
wir eine einfache Klasse:
class CObject
{
public:
CObject()
{ cout << "Konstruktor"
<< endl; }
~CObject() { cout
<< "Destruktor"
<< endl; }
void print() {
cout <<
"Print"
<< endl; }
};
Hier sind die Funktionen inline
deklariert, d.h. nicht separat in einer eigenen C++-Datei.
inline-Funktionen werden vom Compiler durch cut-and-paste direkt in den
Programmcode, der sie verwendet, eingefügt. Das bietet unter
Umständen Geschwindigkeitsvorteile. In unserem Fall sind die
Funktionen direkt in der Klasse implementiert, um den Code kurz und
übersichtlich zu halten.
Das Objekt gibt bei Konstruktion und Destruktion
etwas aus und besitzt über dies hinaus die Funktion print,
die von außen aufgerufen werden kann. Sehen wir und also an,
was passiert, wenn wir ein Objekt erzeugen, darauf zugreifen und es
wieder löschen.
void
dynamicObject()
{
// einzelnes Objekt erzeugen
CObject* o = new
CObject();
// Objektfuntkion aufrufen
(*o).print();
// Dasselbe in einfacherer
Notation
o->print();
// Objekt löschen
(ohne eckige Klammern)
delete o;
}
Beim Erzeugen durch new
wird zunächst Speicher für das Objekt angelegt und
danach der Konstruktor aufgerufen. In unserem Fall wird der
Standardkonstruktor aufgerufen, also ein Konstruktor ohne
Argumente. Anschließend besitzen wir einen Zeiger
auf unser Objekt.
Um auf die öffentlichen Elemente des Objekts
zuzugreifen, verwenden wir danach normalerweise die Notation
objekt->element. Dasselbe
ließe sich auch als (*objekt).element
schreiben, was umständlicher aussieht, aber denselben Zweck
erfüllt.
Am Ende der Methode wird das einzelne Objekt durch den
Befehl delete
gelöscht. Dabei wird zuerst der Destruktor aufgerufen und
danach der Speicher des Objekts wieder freigegeben. Da es sich nicht um ein Array handelt, wird der
delete-Befehl ohne eckige Klammern verwendet.
Worin liegt der
Unterschied? Dazu das letzte Beispiel.
Objekt-Arrays
void
objectArray(unsigned int size)
{
// Array von Objekten
erzeugen
// (Konstruktor wird
aufgerufen)
CObject* o = new
CObject[size];
// auf Objekte zugreifen
for (unsigned
int i = 0; i < size; i++)
{
o[i].print();
}
// Objekte löschen
(mit eckigen
Klammern)
// (Destruktoren werden
aufgerufen)
delete[] o;
}
Für die neu erzeugten Objekte unseres Arrays
wird nach der dynamischen Speicherbelegung jeweils der
Standardkonstruktor aufgerufen. Auf die Elemente können wir
dann über die normale Pointer-Notation mit zwischengestelltem
Punkt zugreifen.
Gelöscht wird das Array mit delete[],
also mit eckigen Klammern wie beim einfachen Array davor. Hierbei wird
zunächst für jedes Objekt der Destruktor aufgerufen
und danach der Speicher des gesamten Arrays wieder freigegeben.
Würden wir die eckigen Klammern hinter delete
vergessen, würde der Compiler davon ausgehen,
dass es sich um ein einzelnes Objekt handelt!
Selbständige
Programmierung
- Ändere im letzten Beispiel den
delete-Operator. Lasse die eckigen Klammern weg, und sieh zu, was
passiert.
- Ändere im vorletzen Beispiel den
delete-Operator. Füge Klammern hinzu uns sieh, was passiert.
- Für die Freaks: Programmiere eine Funktion,
welche die Zeiger-Notation *p++ = 0;
verwendet, um ein Array mit Nullen zu initialisieren. Lösche
danach das Array korrekt mit
delete[].
Was ist zu beachten?
zurück