Objektlebenszeit und Ressourcenverwaltung (RAII)

Im Gegensatz zu verwalteten Sprachen hat C++ keine automatische Garbage Collection. Dies ist ein interner Prozess, der Heapspeicher und andere Ressourcen frei gibt, während ein Programm ausgeführt wird. Ein C++-Programm ist für die Rückgabe aller erworbenen Ressourcen an das Betriebssystem verantwortlich. Wenn eine nicht verwendete Ressource nicht veröffentlicht wird, wird dies als "Leak" bezeichnet. Durch Datenlecks verfügbare Ressourcen sind für andere Programme erst verfügbar, wenn der Prozess beendet wird. Insbesondere Speicherverlusten sind eine häufige Ursache für Fehler bei der Programmierung im C-Stil.

Modern C++ vermeidet die Verwendung von Heapspeicher so weit wie möglich, indem Objekte auf dem Stapel deklariert werden. Wenn eine Ressource zu groß für den Stapel ist, sollte sie im Besitz eines Objekts sein. Wenn das Objekt initialisiert wird, ruft es die Ressource ab, die es besitzt. Das -Objekt ist dann für die Freigabe der Ressource im Destruktor verantwortlich. Das besitzende Objekt selbst wird auf dem Stapel deklariert. Das Prinzip, dass Objekte Ressourcen besitzen, wird auch als "Ressourcenerwerb ist Initialisierung" oder RAII bezeichnet.

Wenn ein Ressourcen besitzende Stapelobjekt den Gültigkeitsbereich übergeht, wird sein 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-Arbeitsspeicherressourcen gleichmäßig verarbeiten.

Das folgende Beispiel zeigt ein einfaches w -Objekt. Sie wird im Stapel im Funktionsumfang deklariert und am Ende des Funktionsblocks zerstört. Das w -Objekt besitzt keine Ressourcen (z. B. mit Heap zugewiesener Arbeitsspeicher). Sein einziger Member wird selbst im Stapel deklariert und geht zusammen mit g einfach aus dem Gültigkeitsbereich w heraus. Im Destruktor ist kein spezieller widget Code erforderlich.

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 besitzt eine Speicherressource und muss daher Code im Destruktor enthalten, w um den Arbeitsspeicher 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, das vorherige Beispiel zu schreiben: mithilfe eines intelligenten Zeigers aus der Standardbibliothek. Der intelligente Zeiger verarbeitet die Zuordnung und Löschung des Speichers, der er besitzt. Durch die Verwendung eines intelligenten Zeigers entfällt 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

Durch die Verwendung intelligenter Zeiger für die Speicherzuordnung können Sie das Potenzial von Speicherverlusten beseitigen. Dieses Modell funktioniert für andere Ressourcen, z. B. Dateihandles oder Sockets. Sie können Ihre eigenen Ressourcen auf ähnliche Weise in Ihren Klassen verwalten. Weitere Informationen finden Sie unter Intelligente Zeiger.

Durch den Entwurf von C++ wird sichergestellt, dass Objekte zerstört werden, wenn sie sich nicht mehr im Gültigkeitsbereich befinden. Das heißt, sie werden zerstört, wenn Blöcke beendet werden, in umgekehrter Reihenfolge der Konstruktion. Wenn ein Objekt zerstört wird, werden seine Basen und Member in einer bestimmten Reihenfolge zerstört. Objekte, die außerhalb eines Blocks im globalen Gültigkeitsbereich deklariert werden, 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