Dynamische Speicherverwaltung: new und delete
Heap-Zuweisungen, sicheres new/delete und erste Schritte in Richtung RAII.
In C++ ist der Speicher in zwei Hauptbereiche unterteilt: Stack (Stapelspeicher) und Heap (dynamischer Speicher).
Während der Stack automatisch verwaltet wird, wird der Speicher im Heap manuell vom Programmierer kontrolliert.
Dynamische Speicherverwaltung wird verwendet, um während der Programmausführung Speicher zu reservieren und wieder freizugeben.
In diesem Artikel lernen wir die Verwendung von new und delete zur dynamischen Speicherverwaltung im Detail kennen.
1. Unterschied zwischen Stack und Heap
| Eigenschaft | Stack (Automatisch) | Heap (Dynamisch) |
|---|---|---|
| Verwaltung | Automatisch (vom Compiler) | Manuell (vom Programmierer) |
| Lebensdauer | Wird automatisch gelöscht, wenn die Funktion endet | Bleibt bestehen, bis sie manuell gelöscht wird |
| Geschwindigkeit | Sehr schnell | Langsamer (dynamische Verwaltung) |
| Speichergröße | Begrenzt | Größer |
| Verwendung | Lokale Variablen | Dynamische Objekte, Arrays |
2. Speicherreservierung mit dem Schlüsselwort new
Der Operator new reserviert dynamisch Speicher im Heap und gibt die Adresse des reservierten Bereichs zurück.
Diese Adresse wird normalerweise einer Pointer-Variablen zugewiesen.
#include <iostream>
using namespace std;
int main() {
int *ptr = new int; // Speicher für eine int-Variable reservieren
*ptr = 42;
cout << "Wert: " << *ptr << endl;
delete ptr; // Speicher freigeben
ptr = nullptr; // Zeiger sicher machen
}
Hinweis: Speicher, der mit new reserviert wurde, muss mit delete manuell freigegeben werden.
Andernfalls entsteht ein Speicherleck (memory leak).
3. Dynamische Arrays mit new
Um ein dynamisches Array zu erstellen, wird new[] verwendet.
Die Größe kann zur Laufzeit bestimmt werden.
#include <iostream>
using namespace std;
int main() {
int n;
cout << "Wie viele Elemente soll das Array haben? ";
cin >> n;
int *array = new int[n]; // Array mit n Elementen erstellen
for (int i = 0; i < n; i++)
array[i] = (i + 1) * 10;
cout << "Array: ";
for (int i = 0; i < n; i++)
cout << array[i] << " ";
delete[] array; // Speicher freigeben
array = nullptr;
}
Die Verwendung von delete[] ist wichtig;
das normale delete gilt nur für einzelne Variablen, nicht für Arrays.
4. Schritte der dynamischen Speicherverwaltung
- new → Reserviert Speicher im Heap.
- Pointer → Speichert die Adresse des reservierten Speichers.
- Daten werden verarbeitet oder gefüllt.
- delete / delete[] → Gibt den Speicher frei.
- Pointer = nullptr → Verhindert „dangling pointers“ (hängende Zeiger).
5. Was ist ein Speicherleck (Memory Leak)?
Wenn dynamisch reservierter Speicher nicht freigegeben wird, sammeln sich ungenutzte Speicherbereiche an. Dies nennt man Speicherleck und kann bei langfristig laufenden Programmen zu Leistungsproblemen führen.
void SchlechtesBeispiel() {
int *p = new int(10);
// delete p; // Vergessen! Speicherleck
}
Lösung: Speicher immer mit delete freigeben.
6. nullptr und Speichersicherheit
Vor C++11 wurden leere Pointer normalerweise mit NULL dargestellt.
In modernem C++ sollte stattdessen das typsichere Schlüsselwort nullptr verwendet werden.
int *p = nullptr;
if (p == nullptr) {
cout << "Der Pointer zeigt auf keine Adresse." << endl;
}
Die Verwendung von nullptr hilft, ungültige Speicherzugriffe und Segmentation Faults zu vermeiden.
7. Dynamischer Speicher mit Funktionen
Speicher kann dynamisch erstellt und an Funktionen übergeben werden. Diese Technik ist besonders nützlich bei der Arbeit mit dynamischen Arrays.
void Fuellen(int *p, int n) {
for (int i = 0; i < n; i++)
p[i] = (i + 1) * 2;
}
int main() {
int *array = new int[5];
Fuellen(array, 5);
for (int i = 0; i < 5; i++)
cout << array[i] << " ";
delete[] array;
}
8. Objekte mit new erstellen
new kann nicht nur für Grunddatentypen, sondern auch für Klassenobjekte verwendet werden.
#include <iostream>
using namespace std;
class Student {
public:
string name;
Student(string n) : name(n) {
cout << name << " erstellt." << endl;
}
~Student() {
cout << name << " gelöscht." << endl;
}
};
int main() {
Student *p = new Student("Ali");
delete p; // Destruktor wird aufgerufen
}
Beim Löschen eines mit new erstellten Objekts wird automatisch der Destruktor der Klasse aufgerufen.
9. Smart Pointer (Moderner C++-Ansatz)
Mit C++11 wurden Smart Pointer als Alternative zur manuellen Speicherfreigabe eingeführt. Sie sorgen für eine automatische Speicherverwaltung.
- std::unique_ptr → Exklusive Besitzrechte (nicht kopierbar)
- std::shared_ptr → Geteilte Besitzrechte (Referenzzählung)
- std::weak_ptr → Schwache Referenz auf geteilte Pointer
#include <iostream>
#include <memory>
using namespace std;
int main() {
unique_ptr<int> p = make_unique<int>(100);
cout << *p << endl; // 100
}
Smart Pointer geben den Speicher automatisch frei, wenn das Objekt den Gültigkeitsbereich verlässt. Dadurch werden Speicherlecks effektiv vermieden.
10. TL;DR
new→ Reserviert Speicher im Heap und gibt die Adresse zurück.delete→ Gibt Speicher frei (für einzelne Variablen).new[] / delete[]→ Wird für dynamische Arrays verwendet.nullptr→ Sichere Definition für leere Pointer.- Um Speicherlecks zu vermeiden, sollte jedem
neweindeleteentsprechen. std::unique_ptrundstd::shared_ptrsind moderne Alternativen.- Alle Beispiele können in Visual Studio 2022 oder GCC 11+ kompiliert werden.