481 Stimmen

Was ist mit "Resource Acquisition is Initialization (RAII)" gemeint?

Was ist mit "Resource Acquisition is Initialization (RAII)" gemeint?

17 Stimmen

Das ist es, was es für mich ausmacht. stroustrup.com/bs_faq2.html#finally

4 Stimmen

Microsoft-Referenz mit 3 Sätzen und 2 Beispielen aber sehr übersichtlich! msdn.microsoft.com/de-us/library/hh438480.aspx

569voto

the_mandrill Punkte 28354

Es ist ein wirklich schrecklicher Name für ein unglaublich leistungsfähiges Konzept und vielleicht eines der wichtigsten Dinge, die C++-Entwickler vermissen, wenn sie zu anderen Sprachen wechseln. Es hat eine kleine Bewegung gegeben, die versucht, dieses Konzept umzubenennen in Umfangsgebundenes Ressourcenmanagement Allerdings scheint es sich noch nicht durchgesetzt zu haben.

Wenn wir "Ressource" sagen, meinen wir nicht nur Speicher - es können auch Datei-Handles, Netzwerk-Sockets, Datenbank-Handles, GDI-Objekte usw. sein. Kurz gesagt, Dinge, die wir nur begrenzt zur Verfügung haben und deren Verwendung wir daher kontrollieren können müssen. Der "Scope-bound"-Aspekt bedeutet, dass die Lebensdauer des Objekts an den Scope einer Variablen gebunden ist, so dass der Destruktor die Ressource freigibt, wenn die Variable den Scope verlässt. Eine sehr nützliche Eigenschaft dieses Aspekts ist, dass er 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 kann man

class ManagedResourceHandle {
public:
   ManagedResourceHandle(RawResourceHandle* rawHandle_) : rawHandle(rawHandle_) {};
   ~ManagedResourceHandle() {delete rawHandle; }
   ... // omitted operator*, etc
private:
   RawResourceHandle* rawHandle;
};

ManagedResourceHandle handle(createNewResource());
handle->performInvalidOperation();

Im letzteren Fall werden die lokalen Variablen zerstört, wenn die Ausnahme ausgelöst und der Stapel abgewickelt wird, wodurch sichergestellt wird, dass unsere Ressource aufgeräumt wird und nicht ausläuft.

3 Stimmen

@the_mandrill: Ich habe ideone.com/1Jjzuc dieses Programm ausprobiert. Aber es gibt keinen Destruktor-Aufruf. In tomdalling.com/blog/software-design/ steht, dass C++ garantiert, dass der Destruktor von Objekten auf dem Stack aufgerufen wird, auch wenn eine Ausnahme geworfen wird. Warum also wurde der Destruktor hier nicht ausgeführt? Ist meine Ressource durchgesickert oder wird sie nie freigegeben oder freigegeben?

20 Stimmen

Eine Ausnahme wird ausgelöst, aber Sie fangen sie nicht ab, so dass die Anwendung beendet wird. Wenn Sie die Anwendung mit einem try { } catch () {} einschließen, funktioniert sie wie erwartet: ideone.com/xm2GR9

3 Stimmen

Nicht ganz sicher, ob Scope-Bound ist hier die beste Namenswahl, da Speicherklassenspezifizierer zusammen mit der Umfang bestimmt die Speicherdauer einer Entität. Die Einschränkung auf scope-bound ist vielleicht eine sinnvolle Vereinfachung, aber nicht 100%ig präzise

163voto

Péter Török Punkte 111735

Dies ist eine Programmiersprache, die kurz gesagt bedeutet, dass Sie

  • eine Ressource in einer Klasse kapseln (deren Konstruktor normalerweise - aber nicht notwendigerweise** - die Ressource erwirbt und deren Destruktor sie immer freigibt)
  • die Ressource über eine lokale Instanz der Klasse verwenden*
  • die Ressource wird automatisch freigegeben, wenn das Objekt den Anwendungsbereich verlässt

Dies garantiert, dass die Ressource, egal was passiert, während sie in Gebrauch ist, irgendwann freigegeben wird (sei es durch normale Rückkehr, Zerstörung des sie enthaltenden Objekts oder durch eine ausgelöste Ausnahme).

Dies ist eine weit verbreitete gute Praxis in C++, da es nicht nur ein sicherer Weg ist, mit Ressourcen umzugehen, sondern auch Ihren Code viel sauberer macht, da Sie keinen Fehlerbehandlungscode mit der Hauptfunktionalität mischen müssen.

* Aktualisierung: "local" kann eine lokale Variable oder eine nichtstatische Mitgliedsvariable einer Klasse bedeuten. Im letzteren Fall wird die Mitgliedsvariable mit ihrem Eigentümerobjekt initialisiert und zerstört.

** Update2: Wie @sbi feststellte, kann die Ressource - obwohl sie oft innerhalb des Konstruktors zugewiesen wird - auch außerhalb zugewiesen und als Parameter übergeben werden.

2 Stimmen

AFAIK bedeutet das Akronym nicht, dass sich das Objekt in einer lokalen (Stack-)Variable befinden muss. Es könnte eine Mitgliedsvariable eines anderen Objekts sein, und wenn das "haltende" Objekt zerstört wird, wird auch das Mitgliedsobjekt zerstört und die Ressource freigegeben. Ich denke, das Akronym bedeutet eigentlich nur, dass es keine open() / close() Methoden zur Initialisierung und Freigabe der Ressource, nur der Konstruktor und der Destruktor, so dass das "Halten" der Ressource nur die Lebensdauer des Objekts ist, unabhängig davon, ob diese Lebensdauer durch den Kontext (Stack) oder explizit (dynamische Zuweisung) gehandhabt wird

2 Stimmen

Es steht nirgends, dass die Ressource im Konstruktor erworben werden muss. Dateistreams, Strings und andere Container tun das, aber die Ressource könnte genauso gut sein bestanden an den Konstruktor übergeben, wie es bei intelligenten Zeigern normalerweise der Fall ist. Da Ihre Antwort die am meisten bewertete ist, sollten Sie dies vielleicht beheben.

0 Stimmen

Es handelt sich nicht um ein Akronym, sondern um eine Abkürzung. Wenn ich mich recht erinnere, wird es von den meisten Menschen als "ar ey ay ay" ausgesprochen, so dass es sich nicht wirklich als Akronym eignet, wie z. B. DARPA, das als DARPA ausgesprochen und nicht buchstabiert wird. Außerdem würde ich sagen, dass RAII eher ein Paradigma als eine bloße Redewendung ist.

62voto

sbi Punkte 211669

"RAII" steht für "Resource Acquisition is Initialization" (Ressourcenerwerb ist Initialisierung) und ist eigentlich eine falsche Bezeichnung, da es sich nicht um Ressourcen Erwerb (und die Initialisierung eines Objekts), mit der es sich befasst, sondern Freisetzung von die Ressource (mit Hilfe von Zerstörung eines Objekts).
Aber RAII ist der Name, den wir bekommen haben, und er bleibt haften.

Im Kern besteht das Idiom aus der Kapselung von Ressourcen (Speicherabschnitte, geöffnete Dateien, nicht gesperrte Mutexe, usw.) in lokale, automatische Objekte und dass der Destruktor dieses Objekts die Ressource freigibt, wenn das Objekt am Ende des Bereichs, zu dem es gehört, zerstört wird:

{
  raii obj(acquire_resource());
  // ...
} // obj's dtor will call release_resource()

Natürlich sind Objekte nicht immer lokale, automatische Objekte. Sie können 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 "Smart Pointer" bezeichnet.

Hiervon gibt es viele Varianten. In den ersten Codeschnipseln stellt sich zum Beispiel die Frage, was passieren würde, wenn jemand Folgendes 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 und im nächsten C++-Standard enthalten sein wird, tut dies.
Noch so ein kluger Hinweis, std::shared_ptr verfügt über "Shared Ownership" für die Ressource (ein dynamisch zugewiesenes Objekt), die es enthält. Das heißt, es kann frei kopiert werden und alle Kopien beziehen sich auf dasselbe Objekt. Der intelligente Zeiger verfolgt, wie viele Kopien auf dasselbe Objekt verweisen, und löscht es, wenn die letzte Kopie zerstört wird.
Eine dritte Variante wird vorgestellt von std::auto_ptr die eine Art von Bewegungs-Semantik implementiert: Ein Objekt gehört nur einem Zeiger, und der Versuch, ein Objekt zu kopieren, führt (durch Syntax-Hackerei) dazu, dass das Eigentum an dem Objekt auf das Ziel des Kopiervorgangs übertragen wird.

4 Stimmen

std::auto_ptr ist eine veraltete Version von std::unique_ptr . std::auto_ptr eine Art von simulierter Bewegungs-Semantik, soweit dies in C++98 möglich war, std::unique_ptr verwendet die neue Move-Semantik von C++11. Die neue Klasse wurde geschaffen, weil die Move-Semantik von C++11 expliziter ist (erfordert std::move ausgenommen temporäre Kopien), während sie für alle Kopien von non-const in std::auto_ptr .

0 Stimmen

@JiahaoCai: Einmal, vor vielen Jahren (im Usenet), sagte Stroustrup selbst dies.

41voto

elmiomar Punkte 1577

Die Lebensdauer eines Objekts wird durch seinen Geltungsbereich bestimmt. Manchmal ist es jedoch notwendig oder nützlich, ein Objekt zu erstellen, das unabhängig von dem Bereich lebt, in dem es erstellt wurde. In C++ ist der Operator new wird verwendet, um ein solches Objekt zu erstellen. Und um das Objekt zu zerstören, wird der Operator delete verwendet werden können. Vom Operator erstellte Objekte new werden dynamisch zugewiesen, d. h. im dynamischen Speicher (auch als Haufen ou freies Lager ). So kann ein Objekt, das von new existiert so lange, bis sie explizit mit delete .

Einige Fehler, die bei der Verwendung von new y delete sind:

  • Durchgesickertes Objekt (oder Speicher): mit new ein Objekt zuzuweisen und zu vergessen delete das Objekt.
  • Vorzeitig löschen (oder hängender Verweis ): hält einen weiteren Zeiger auf ein Objekt, delete das Objekt, und verwenden Sie dann den anderen Zeiger.
  • Doppeltes Löschen : Versuch einer delete ein Objekt zweimal.

Im Allgemeinen werden skalierte Variablen bevorzugt. RAII kann jedoch als Alternative zu new y delete um ein Objekt unabhängig von seinem Geltungsbereich leben zu lassen. Eine solche Technik besteht darin, den Zeiger auf das Objekt, das auf dem Heap zugewiesen wurde, in einen handle/manager object . Letzteres hat einen Destruktor, der sich um die Zerstörung des Objekts kümmert. Damit wird sichergestellt, dass das Objekt jeder Funktion zur Verfügung steht, die darauf zugreifen möchte, und dass das Objekt zerstört wird, wenn die Lebensdauer der Griffobjekt endet, ohne dass eine explizite Bereinigung erforderlich ist.

Beispiele aus der C++-Standardbibliothek, die RAII verwenden, sind std::string y std::vector .

Betrachten Sie dieses Stück Code:

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 ihm Elemente hinzufügen, müssen Sie sich nicht um die Zuweisung und Freigabe solcher Elemente kümmern. Der Vektor verwendet new um Platz für seine Elemente auf dem Heap zuzuweisen, und delete um diesen Platz freizugeben. Als Nutzer von vector sind Ihnen die Implementierungsdetails egal und Sie vertrauen darauf, dass vector nicht ausläuft. In diesem Fall ist der Vektor der Griffobjekt seiner Elemente.

Weitere Beispiele aus der Standardbibliothek, die RAII verwenden, sind std::shared_ptr , std::unique_ptr であり、また std::lock_guard .

Ein anderer Name für diese Technik ist SBRM , kurz für Umfangsgebundenes Ressourcenmanagement .

3 Stimmen

"SBRM" macht für mich viel mehr Sinn. Ich bin zu dieser Frage gekommen, weil ich dachte, ich hätte RAII verstanden, aber der Name hat mich verwirrt. Als ich dann hörte, dass es stattdessen "Scope-Bound Resource Management" heißt, wurde mir sofort klar, dass ich das Konzept tatsächlich verstanden hatte.

2 Stimmen

Ich bin mir nicht sicher, warum dies nicht als Antwort auf die Frage markiert wurde. Es ist eine sehr gründliche und gut geschriebene Antwort, danke @elmiomar

17voto

Dennis Punkte 51330

Das Buch C++-Programmierung mit aufgedeckten Entwurfsmustern beschreibt RAII als:

  1. Beschaffung aller Ressourcen
  2. Einsatz von Ressourcen
  3. Freisetzung von Ressourcen

Wo

  • Ressourcen sind als Klassen implementiert, und alle Zeiger haben Klassenumhüllungen 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.

1 Stimmen

@Brandin Ich habe meinen Beitrag so bearbeitet, dass sich die Leser auf den Inhalt konzentrieren können, auf den es ankommt, anstatt über die Grauzone des Urheberrechts zu debattieren, was eine faire Nutzung darstellt.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X