Objektlebenszeit und Ressourcenverwaltung (RAII)

Im Gegensatz zu verwalteten Sprachen verfügt C++ nicht über die automatische Garbage Collection, ein interner Prozess, der Heap-Speicher und andere Ressourcen als Programm ausführt. Ein C++-Programm ist dafür verantwortlich, alle erworbenen Ressourcen an das Betriebssystem zurückzugeben. Fehler beim Freigeben einer nicht verwendeten Ressource wird als Leck bezeichnet. Nicht freigegebene Ressourcen sind für andere Programme nicht verfügbar, bis der Prozess beendet wird. Speicherlecks sind insbesondere eine häufige Ursache für Fehler bei der Programmierung im C-Stil.

Moderne C++ vermeiden die Verwendung des Heap-Speichers so weit wie möglich, indem Objekte im Stapel deklariert werden. Wenn eine Ressource für den Stapel zu groß ist, sollte sie einem Objekt gehören . Wenn das Objekt initialisiert wird, erhält es die Ressource, die es besitzt. Das Objekt ist dann für das Freigeben der Ressource in seinem Destruktor verantwortlich. Das besitzereigene Objekt selbst wird im Stapel deklariert. Das Prinzip, dass Objekte Ressourcen besitzen, wird auch als "Ressourcenerwerb ist Initialisierung" oder RAII bezeichnet.

Wenn ein ressourceneigenes Stapelobjekt außerhalb des Gültigkeitsbereichs liegt, wird dessen Destruktor automatisch aufgerufen. Auf diese Weise ist die Garbage Collection in C++ eng mit der Objektlebensdauer verknüpft und deterministisch. Eine Ressource wird immer an einem bekannten Punkt im Programm freigegeben, den Sie steuern können. Nur deterministische Destruktoren wie in C++ können Arbeitsspeicher- und Nicht-Speicherressourcen gleichermaßen verarbeiten.

Das folgende Beispiel zeigt ein einfaches Objekt w. Er wird im Stapel im Funktionsbereich deklariert und am Ende des Funktionsblocks zerstört. Das Objekt w besitzt keine Ressourcen (z. B. heap zugeordneten Speicher). Sein einziges Mitglied g wird selbst auf dem Stapel deklariert und geht einfach aus dem Bereich zusammen.w Im Destruktor ist kein spezieller Code erforderlich widget .

class widget {
private:
    gadget g;   // lifetime automatically tied to enclosing object
public:
    void draw();
};

void functionUsingWidget () {
    widget w;   // lifetime automatically tied to enclosing scope
                // constructs w, including the w.g gadget member
    // ...
    w.draw();
    // ...
} // automatic destruction and deallocation for w and w.g
  // automatic exception safety,
  // as if "finally { w.dispose(); w.g.dispose(); }"

Im folgenden Beispiel w besitzt eine Speicherressource und muss daher Code in seinem Destruktor enthalten, um den Speicher zu löschen.

class widget
{
private:
    int* data;
public:
    widget(const int size) { data = new int[size]; } // acquire
    ~widget() { delete[] data; } // release
    void do_something() {}
};

void functionUsingWidget() {
    widget w(1000000);  // lifetime automatically tied to enclosing scope
                        // constructs w, including the w.data member
    w.do_something();

} // automatic destruction and deallocation for w and w.data

Seit C++11 gibt es eine bessere Möglichkeit zum Schreiben des vorherigen Beispiels: Mithilfe eines intelligenten Zeigers aus der Standardbibliothek. Der intelligente Zeiger behandelt die Zuordnung und Löschung des speichereigenen Speichers. Die Verwendung eines intelligenten Zeigers beseitigt die Notwendigkeit eines expliziten Destruktors in der widget Klasse.

#include <memory>
class widget
{
private:
    std::unique_ptr<int[]> data;
public:
    widget(const int size) { data = std::make_unique<int[]>(size); }
    void do_something() {}
};

void functionUsingWidget() {
    widget w(1000000);  // lifetime automatically tied to enclosing scope
                        // constructs w, including the w.data gadget member
    // ...
    w.do_something();
    // ...
} // automatic destruction and deallocation for w and w.data

Wenn Sie intelligente Zeiger für die Speicherzuweisung verwenden, können Sie das Potenzial für Speicherverluste beseitigen. Dieses Modell funktioniert für andere Ressourcen, z. B. Dateihandles oder Sockets. Sie können Ihre eigenen Ressourcen in Ihren Kursen auf ähnliche Weise verwalten. Weitere Informationen finden Sie unter "Intelligente Zeiger".

Das Design von C++ stellt sicher, dass Objekte zerstört werden, wenn sie außerhalb des Gültigkeitsbereichs liegen. Das heißt, sie werden zerstört, wenn Blöcke verlassen werden, in umgekehrter Reihenfolge des Baus. Wenn ein Objekt zerstört wird, werden seine Basen und Elemente in einer bestimmten Reihenfolge zerstört. Objekte, die außerhalb eines beliebigen Blocks im globalen Bereich deklariert sind, können zu Problemen führen. Es kann schwierig sein, zu debuggen, wenn der Konstruktor eines globalen Objekts eine Ausnahme auslöst.

Siehe auch

Willkommen zurück bei C++
C#-Programmiersprachenreferenz
C++-Standardbibliothek