RAII
Resource Acquisition Is Initialization oder RAII, ist eine C++-Programmiertechnik [1] [2] die den Lebenszyklus einer Ressource, die vor der Verwendung erworben werden muss (allozierter Heap-Speicher, Ausführungs-Thread, geöffneter Socket, geöffnete Datei, gesperrtes Mutex, Festplattenspeicher, Datenbankverbindung - alles, was in begrenzter Menge vorhanden ist) an die Lebensdauer eines Objekts bindet.
RAII garantiert, dass die Ressource für jede Funktion verfügbar ist, die auf das Objekt zugreifen kann (Ressourcenverfügbarkeit ist eine Klasseninvariante , was redundante Laufzeittests eliminiert). Es garantiert auch, dass alle Ressourcen freigegeben werden, wenn die Lebensdauer ihres kontrollierenden Objekts endet, in umgekehrter Reihenfolge der Acquisition. Ebenso werden bei fehlgeschlagener Ressourcenacquisition (wenn der Konstruktor mit einer Exception verlassen wird) alle Ressourcen, die von jedem vollständig konstruierten Member- und Basis-Subobjekt erworben wurden, in umgekehrter Reihenfolge der Initialisierung freigegeben. Dies nutzt die Kernsprachfunktionen ( Objektlebensdauer , Scope-Exit , Initialisierungsreihenfolge und Stack-Unwinding ) aus, um Ressourcenlecks zu eliminieren und Exceptionsicherheit zu garantieren. Ein anderer Name für diese Technik ist Scope-Bound Resource Management (SBRM), nach dem grundlegenden Anwendungsfall, bei dem die Lebensdauer eines RAII-Objekts aufgrund des Scope-Exits endet.
RAII lässt sich wie folgt zusammenfassen:
- kapseln Sie jede Ressource in eine Klasse, wo
-
- der Konstruktor erwirbt die Ressource und stellt alle Klasseninvarianten her oder wirft eine Ausnahme, falls dies nicht möglich ist,
- der Destruktor gibt die Ressource frei und wirft niemals Ausnahmen;
- verwenden Sie die Ressource stets über eine Instanz einer RAII-Klasse, die entweder
-
- hat automatische Speicherdauer oder temporäre Lebensdauer selbst, oder
- hat eine Lebensdauer, die durch die Lebensdauer eines automatischen oder temporären Objekts begrenzt ist.
|
Move-Semantik ermöglicht die Übertragung von Ressourcen und Eigentumsrechten zwischen Objekten, innerhalb und außerhalb von Containern sowie über Threads hinweg, wobei die Ressourcensicherheit gewährleistet wird. |
(since C++11) |
Klassen mit
open()
/
close()
,
lock()
/
unlock()
, oder
init()
/
copyFrom()
/
destroy()
Memberfunktionen sind typische Beispiele für nicht-RAII-Klassen:
std::mutex m; void bad() { m.lock(); // Mutex anfordern f(); // Wenn f() eine Exception wirft, wird der Mutex nie freigegeben if (!everything_ok()) return; // Vorzeitige Rückkehr, der Mutex wird nie freigegeben m.unlock(); // Wenn bad() diese Anweisung erreicht, wird der Mutex freigegeben } void good() { std::lock_guard<std::mutex> lk(m); // RAII-Klasse: Mutex-Anforderung ist Initialisierung f(); // Wenn f() eine Exception wirft, wird der Mutex freigegeben if (!everything_ok()) return; // Vorzeitige Rückkehr, der Mutex wird freigegeben } // Wenn good() normal zurückkehrt, wird der Mutex freigegeben
Die Standardbibliothek
Die C++-Bibliotheksklassen, die ihre eigenen Ressourcen verwalten, folgen RAII: std::string , std::vector , std::jthread (seit C++20) , und viele andere erwerben ihre Ressourcen in Konstruktoren (die bei Fehlern Ausnahmen werfen), geben sie in ihren Destruktoren frei (die niemals werfen) und erfordern keine explizite Bereinigung.
|
Darüber hinaus bietet die Standardbibliothek mehrere RAII-Wrapper zur Verwaltung benutzerdefinierter Ressourcen:
|
(seit C++11) |
Hinweise
RAII gilt nicht für die Verwaltung von Ressourcen, die nicht vor der Nutzung erworben werden: CPU-Zeit, Kernverfügbarkeit, Cache-Kapazität, Entropiepool-Kapazität, Netzwerkbandbreite, Stromverbrauch, Stack-Speicher. Für solche Ressourcen kann ein C++-Klassenkonstruktor keine Ressourcenverfügbarkeit für die Dauer der Objektlebensdauer garantieren, und andere Mittel der Ressourcenverwaltung müssen verwendet werden.