Was ist mit Resource Acquisition is Initialization (RAII) gemeint?
Was ist mit Resource Acquisition is Initialization (RAII) gemeint?
John
der_mandrill
Es ist ein wirklich schrecklicher Name für ein unglaublich mächtiges Konzept und vielleicht eines der wichtigsten Dinge, die C++-Entwickler vermissen, wenn sie zu anderen Sprachen wechseln. Es gab eine gewisse Bewegung, um zu versuchen, dieses Konzept in umzubenennen Bereichsgebundene Ressourcenverwaltungobwohl es sich anscheinend noch nicht durchgesetzt hat.
Wenn wir „Ressource“ sagen, meinen wir nicht nur Speicher – es können Datei-Handles, Netzwerk-Sockets, Datenbank-Handles, GDI-Objekte sein … Kurz gesagt, Dinge, die wir nur begrenzt zur Verfügung haben und daher benötigen ihre Verwendung kontrollieren. Der Aspekt „Scope-bound“ bedeutet, dass die Lebensdauer des Objekts an den Gültigkeitsbereich einer Variablen gebunden ist, sodass der Destruktor die Ressource freigibt, wenn die Variable den Gültigkeitsbereich verlässt. Eine sehr nützliche Eigenschaft davon ist, dass es für eine größere Ausnahmesicherheit sorgt. Vergleichen Sie zum Beispiel dies:
RawResourceHandle* handle=createNewResource();
handle->performInvalidOperation(); // Oops, throws exception
...
deleteResource(handle); // oh dear, never gets called so the resource leaks
Mit dem RAII eins
class ManagedResourceHandle {
public:
ManagedResourceHandle(RawResourceHandle* rawHandle_) : rawHandle(rawHandle_) {};
~ManagedResourceHandle() {delete rawHandle; }
... // omitted operator*, etc
private:
RawResourceHandle* rawHandle;
};
ManagedResourceHandle handle(createNewResource());
handle->performInvalidOperation();
In diesem letzteren Fall werden die lokalen Variablen zerstört, wenn die Ausnahme ausgelöst und der Stapel entladen wird, wodurch sichergestellt wird, dass unsere Ressource bereinigt wird und nicht leckt.
-
@the_mandrill: Ich habe dieses Programm auf ideone.com/1Jjzuc ausprobiert. Aber es gibt keinen Destruktoraufruf. Die tomdalling.com/blog/software-design/… besagt, dass C++ garantiert, dass der Destruktor von Objekten auf dem Stapel aufgerufen wird, selbst wenn eine Ausnahme ausgelöst wird. Also, warum wurde der Destruktor hier nicht ausgeführt? Ist meine Ressource durchgesickert oder wird sie niemals freigegeben oder freigegeben?
– Destruktor
20. August 2015 um 16:40 Uhr
-
Eine Ausnahme wird ausgelöst, aber Sie fangen sie nicht ab, sodass die Anwendung beendet wird. Wenn Sie mit try { } catch () {} umbrechen, funktioniert es wie erwartet: ideone.com/xm2GR9
– der_Mandrill
21. August 2015 um 10:41 Uhr
-
Nicht ganz sicher, ob
Scope-Bound
ist hier die beste Namenswahl seit Speicherklassenbezeichnern zusammen mit der Geltungsbereich bestimmt die Speicherdauer einer Entität. Die Einschränkung auf den Umfang ist vielleicht eine nützliche Vereinfachung, aber nicht 100% genau– Klick mich
12. April 2018 um 8:53 Uhr
-
Aber wie erklärt man
RA is initialization
im Originalnamen? Was ich unter RAII verstehe, ist, dass es in der Verantwortung jedes Objekts liegt, sich um seine Löschung zu kümmern, sobald es außerhalb des Geltungsbereichs liegt. Für mich passt das nicht zusammenis initialization
weil alles mit dem Destruktor zusammenhängt. Ich bin immer noch verwirrt von diesem Idiomnamen.– nowox
11. November 2020 um 12:49 Uhr
-
“… die wichtigsten Dinge, die C++-Entwickler vermissen …” Ist das nicht ähnlich wie try-with-resources in Java? Es scheint das gleiche Problem zu lösen, und ich sehe keinen Vor- oder Nachteil von RAII im Vergleich zu Javas Lösung.
– marc.guenther
17. Dezember 2021 um 15:58 Uhr
Peter Török
Dies ist eine Programmiersprache, die kurz bedeutet, dass Sie
- eine Ressource in eine Klasse kapseln (deren Konstruktor normalerweise – aber nicht notwendigerweise** – die Ressource erwirbt und ihr Destruktor sie immer freigibt)
- Verwenden Sie die Ressource über eine lokale Instanz der Klasse*
- Die Ressource wird automatisch freigegeben, wenn das Objekt den Gültigkeitsbereich verlässt
Dies garantiert, dass alles, was passiert, während die Ressource verwendet wird, letztendlich freigegeben wird (ob durch normale Rückgabe, Zerstörung des enthaltenden Objekts oder eine ausgelöste Ausnahme).
Es ist eine weit verbreitete bewährte Methode in C++, da es nicht nur eine sichere Methode zum Umgang mit Ressourcen darstellt, sondern auch Ihren Code viel sauberer macht, da Sie den Fehlerbehandlungscode nicht mit der Hauptfunktionalität mischen müssen.
*
Aktualisieren: “Lokal” kann eine lokale Variable oder eine nichtstatische Mitgliedsvariable einer Klasse bedeuten. Im letzteren Fall wird die Mitgliedsvariable mit ihrem Besitzerobjekt initialisiert und zerstört.
**
Update2: Wie @sbi betonte, kann die Ressource – obwohl sie oft innerhalb des Konstruktors zugewiesen wird – auch außerhalb zugewiesen und als Parameter übergeben werden.
-
AFAIK, das Akronym bedeutet nicht, dass sich das Objekt auf einer lokalen (Stack-)Variablen befinden muss. Es könnte eine Mitgliedsvariable eines anderen Objekts sein, wenn also das ‘haltende’ Objekt zerstört wird, wird auch das Mitgliedsobjekt zerstört und die Ressource wird freigegeben. Tatsächlich denke ich, dass das Akronym ausdrücklich nur bedeutet, dass es kein gibt
open()
/close()
Methoden zum Initialisieren und Freigeben der Ressource, nur der Konstruktor und der Destruktor, sodass das „Halten“ der Ressource nur die Lebensdauer des Objekts ist, unabhängig davon, ob diese Lebensdauer vom Kontext (Stack) oder explizit (dynamische Zuweisung) behandelt wird.– Javier
23. Februar 2010 um 20:51 Uhr
-
Eigentlich sagt nichts, dass die Ressource im Konstruktor erworben werden muss. Dateistreams, Strings und andere Container tun das, aber die Ressource könnte es genauso gut sein bestanden an den Konstruktor, wie es normalerweise bei intelligenten Zeigern der Fall ist. Da Ihre Antwort am meisten positiv bewertet wurde, möchten Sie dies möglicherweise beheben.
– sbi
8. Oktober 2012 um 20:59 Uhr
-
Es ist kein Akronym, sondern eine Abkürzung. IIRC die meisten Leute sprechen es “ar ey ay ay” aus, also qualifiziert es sich nicht wirklich für ein Akronym wie sagen wir DARPA, das DARPA statt buchstabiert wird. Außerdem würde ich sagen, dass RAII eher ein Paradigma als eine bloße Redewendung ist.
– dtech
5. August 2013 um 10:24 Uhr
-
@ Peter Torok: Ich habe es versucht ideone.com/1Jjzuc dieses Programm. Aber es gibt keinen Destruktoraufruf. Die tomdalling.com/blog/software-design/… besagt, dass C++ garantiert, dass der Destruktor von Objekten auf dem Stack aufgerufen wird, selbst wenn eine Ausnahme ausgelöst wird. Also, warum wurde der Destruktor hier nicht ausgeführt? Ist meine Ressource durchgesickert oder wird sie niemals freigegeben oder freigegeben?
– Destruktor
20. August 2015 um 16:35 Uhr
-
In diesem Beispiel wird die Ausnahme nicht abgefangen, sodass das Programm sofort beendet wird. Wenn Sie die Ausnahme abfangen, wird der Destruktor aufgerufen, wenn der Stapel entladen wird.
– der_Mandrill
12. November 2020 um 16:36 Uhr
sbi
„RAII“ steht für „Resource Acquisition is Initialization“ und ist eigentlich eine ziemlich falsche Bezeichnung, da es sich nicht um eine Ressource handelt Erwerb (und die Initialisierung eines Objekts) betrifft, aber loslassen die Ressource (mittels Zerstörung eines Objektes).
Aber RAII ist der Name, den wir bekommen haben, und er bleibt.
Im Kern bietet das Idiom das Einkapseln von Ressourcen (Speicherblöcke, offene Dateien, entsperrte Mutexe, was auch immer) in lokale, automatische Objekteund dass der Destruktor dieses Objekts die Ressource freigibt, wenn das Objekt am Ende des Bereichs zerstört wird, zu dem es gehört:
{
raii obj(acquire_resource());
// ...
} // obj's dtor will call release_resource()
Natürlich sind Objekte nicht immer lokale, automatische Objekte. Sie könnten auch Mitglieder einer Klasse sein:
class something {
private:
raii obj_; // will live and die with instances of the class
// ...
};
Wenn solche Objekte Speicher verwalten, werden sie oft als “intelligente Zeiger” bezeichnet.
Davon gibt es viele Variationen. Beispielsweise stellt sich in den ersten Codeschnipseln die Frage, was passieren würde, wenn jemand kopieren wollte obj
. Der einfachste Ausweg wäre, das Kopieren einfach zu verbieten. std::unique_ptr<>
ein intelligenter Zeiger, der Teil der Standardbibliothek sein soll, wie sie vom nächsten C++-Standard vorgestellt wird, tut dies.
Ein weiterer solcher intelligenter Zeiger, std::shared_ptr
verfügt über “geteiltes Eigentum” an der Ressource (einem dynamisch zugewiesenen Objekt), die es besitzt. Das heißt, es kann frei kopiert werden und alle Kopien beziehen sich auf dasselbe Objekt. Der intelligente Zeiger verfolgt, wie viele Kopien sich auf dasselbe Objekt beziehen, und löscht es, wenn die letzte zerstört wird.
Eine dritte Variante wird von vorgestellt std::auto_ptr
Dies implementiert eine Art Move-Semantik: Ein Objekt gehört nur einem Zeiger, und der Versuch, ein Objekt zu kopieren, führt (durch Syntax-Hacking) dazu, dass der Besitz des Objekts auf das Ziel der Kopieroperation übertragen wird.
-
std::auto_ptr
ist eine veraltete Version vonstd::unique_ptr
.std::auto_ptr
eine Art simulierte Bewegungssemantik, soweit es in C++98 möglich war,std::unique_ptr
verwendet die neue Move-Semantik von C++11. Eine neue Klasse wurde erstellt, da die Move-Semantik von C++11 expliziter ist (erfordertstd::move
außer von temporär), während es für jede Kopie von non-const in voreingestellt warstd::auto_ptr
.– Jan Hudec
5. August 2013 um 9:40 Uhr
-
@JiahaoCai: Vor vielen Jahren (im Usenet) hat Stroustrup selbst das gesagt.
– sbi
20. Januar 2020 um 18:49 Uhr
Die Lebensdauer eines Objekts wird durch seinen Geltungsbereich bestimmt. Manchmal müssen wir jedoch, oder es ist nützlich, ein Objekt zu erstellen, das unabhängig von dem Bereich lebt, in dem es erstellt wurde. In C++ der Operator new
wird verwendet, um ein solches Objekt zu erstellen. Und um das Objekt zu zerstören, der Betreiber delete
kann verwendet werden. Vom Bediener erstellte Objekte new
werden dynamisch allokiert, d.h. allokiert im dynamischen Speicher (auch genannt Haufen oder kostenlos speichern). Also ein Objekt, das von erstellt wurde new
wird weiter existieren, bis es explizit zerstört wird mit delete
.
Einige Fehler, die bei der Verwendung auftreten können new
und delete
sind:
- Durchgesickertes Objekt (oder Speicher): mit
new
ein Objekt zuordnen und vergessendelete
das Objekt. - Vorzeitig löschen (oder hängende Referenz): Halten eines anderen Zeigers auf ein Objekt,
delete
das Objekt, und verwenden Sie dann den anderen Zeiger. - Doppelt löschen: versuchen
delete
ein Objekt zweimal.
Im Allgemeinen werden Bereichsvariablen bevorzugt. RAII kann jedoch alternativ dazu verwendet werden new
und delete
ein Objekt unabhängig von seinem Geltungsbereich zum Leben erwecken. Eine solche Technik besteht darin, den Zeiger auf das Objekt zu nehmen, das auf dem Heap zugewiesen wurde, und es in a zu platzieren Handle/Manager-Objekt. Letzteres hat einen Destruktor, der sich um die Zerstörung des Objekts kümmert. Dadurch wird sichergestellt, dass das Objekt für alle Funktionen verfügbar ist, die darauf zugreifen möchten, und dass das Objekt zerstört wird, wenn die Lebensdauer der Objekt behandeln endet, ohne dass eine explizite Bereinigung erforderlich ist.
Beispiele aus der C++-Standardbibliothek, die RAII verwenden, sind std::string
und std::vector
.
Betrachten Sie diesen Codeabschnitt:
void fn(const std::string& str)
{
std::vector<char> vec;
for (auto c : str)
vec.push_back(c);
// do something
}
Wenn Sie einen Vektor erstellen und Elemente dorthin schieben, kümmern Sie sich nicht darum, solche Elemente zuzuweisen oder freizugeben. Der Vektor verwendet new
Platz für seine Elemente auf dem Heap zuzuweisen, und delete
um diesen Raum freizugeben. Sie als Benutzer von vector kümmern sich nicht um die Implementierungsdetails und vertrauen darauf, dass vector keine Daten verliert. In diesem Fall ist der Vektor der Objekt behandeln seiner Elemente.
Andere Beispiele aus der Standardbibliothek, die RAII verwenden, sind std::shared_ptr
, std::unique_ptr
und std::lock_guard
.
Ein anderer Name für diese Technik ist SBRMkurz für Bereichsgebundene Ressourcenverwaltung.
Dennis
Das Buch C++ Programming with Design Patterns Revealed beschreibt RAII als:
- Erwerb aller Ressourcen
- Ressourcen nutzen
- Ressourcen freigeben
Woher
-
Ressourcen werden als Klassen implementiert, und alle Zeiger haben Klassen-Wrapper um sich herum (was sie zu intelligenten Zeigern macht).
-
Ressourcen werden durch Aufrufen ihrer Konstruktoren erworben und implizit (in umgekehrter Reihenfolge des Erwerbs) durch Aufrufen ihrer Destruktoren freigegeben.
-
@Brandin Ich habe meinen Beitrag so bearbeitet, dass sich die Leser auf den Inhalt konzentrieren, der wichtig ist, anstatt über die Grauzone des Urheberrechts zu diskutieren, was eine faire Verwendung darstellt.
– Denis
29. Mai 2016 um 13:33 Uhr
Mohammed Moridi
Eine RAII-Klasse besteht aus drei Teilen:
- Die Ressource wird im Destruktor aufgegeben
- Instanzen der Klasse werden dem Stapel zugeordnet
- Die Ressource wird im Konstruktor erfasst. Dieser Teil ist optional, aber üblich.
RAII steht für „Resource Acquisition is initialization“. Im „Ressourcenerwerb“-Teil von RAII beginnen Sie etwas, das später beendet werden muss, wie zum Beispiel:
- Öffnen einer Datei
- Etwas Speicher zuweisen
- Erwerb einer Sperre
Der Teil „is initialization“ bedeutet, dass die Übernahme innerhalb des Konstruktors einer Klasse erfolgt.
-
@Brandin Ich habe meinen Beitrag so bearbeitet, dass sich die Leser auf den Inhalt konzentrieren, der wichtig ist, anstatt über die Grauzone des Urheberrechts zu diskutieren, was eine faire Verwendung darstellt.
– Denis
29. Mai 2016 um 13:33 Uhr
Dmitri Pawlow
Die manuelle Speicherverwaltung ist ein Albtraum, den Programmierer seit der Erfindung des Compilers immer wieder versuchen zu vermeiden. Programmiersprachen mit Garbage Collector machen das Leben einfacher, aber auf Kosten der Performance. In diesem Artikel – Den Garbage Collector eliminieren: Der RAII-WegToptal-Ingenieur Peter Goodspeed-Niklaus gibt uns einen Einblick in die Geschichte der Müllsammler und erklärt, wie Vorstellungen von Eigentum und Leihe dazu beitragen können, Müllsammler zu eliminieren, ohne ihre Sicherheitsgarantien zu gefährden.
en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization
– Benutzer151323
23. Februar 2010 um 20:39 Uhr
Das treibt es für mich nach Hause. stroustrup.com/bs_faq2.html#endlich
– Hal Kanarienvogel
21. Mai 2013 um 22:45 Uhr
Microsoft Referenz mit 3 Sätzen und 2 Beispielen noch sehr übersichtlich! msdn.microsoft.com/en-us/library/hh438480.aspx
– Gab是好人
17. November 2016 um 21:58 Uhr