Was ist RAII? Beispiele?

19

Immer wenn der Begriff RAII verwendet wird, spricht man eigentlich von Dekonstruktion anstatt von Initialisierung. Ich glaube, ich habe ein grundlegendes Verständnis dafür, was es bedeuten könnte, bin mir aber nicht ganz sicher. Außerdem: Ist C ++ die einzige RAII-Sprache? Was ist mit Java oder C # /. NET?

Felix Dombek
quelle

Antworten:

25

Ressourcenerfassung ist Initialisierung bedeutet, dass Objekte sich selbst als vollständiges Paket betrachten und nicht erwarten sollten, dass ein anderer Code einer Instanz mitteilt. Dies bedeutet normalerweise, dass der Destruktor etwas Bedeutendes enthält. Dies bedeutet auch, dass Sie eine Klasse speziell zum Verwalten von Ressourcen schreiben und wissen, dass Sie unter bestimmten, schwer vorhersehbaren Umständen, z. B. beim Auslösen von Ausnahmen, mit der Ausführung von Destruktoren rechnen können.

Angenommen, Sie möchten einen Code schreiben, in dem Sie den Windows-Cursor in einen Wartecursor (Sanduhr, Donut-of-Not-Working-Cursor usw.) ändern, Ihre Aufgaben erledigen und ihn dann zurücksetzen. Und sagen Sie auch, dass "do your stuff" eine Ausnahme auslösen könnte. Die RAII-Methode besteht darin, eine Klasse zu erstellen, deren ctor den Cursor zum Warten setzt, deren eine "echte" Methode das tut, was Sie wollen, und deren dtor den Cursor zurücksetzt. Ressourcen (in diesem Fall der Cursor-Status) sind an den Bereich eines Objekts gebunden. Wenn Sie die Ressource erwerben, initialisieren Sie ein Objekt. Sie können sich darauf verlassen, dass das Objekt zerstört wird, wenn Ausnahmen ausgelöst werden, und das bedeutet, dass Sie sich darauf verlassen können, dass die Ressource bereinigt wird.

Wenn Sie RAII gut verwenden, brauchen Sie es nicht finally. Natürlich beruht es auf deterministischer Zerstörung, die Sie in Java nicht haben können. Sie können eine Art deterministische Zerstörung in C # und VB.NET mit bekommen using.

Kate Gregory
quelle
4
Ich denke, das ist es, worauf Sie hinaus wollen, aber vielleicht möchten Sie hinzufügen, dass der Grund, warum Java und C # RAII nicht unterstützen, der Garbage Collector ist. In C ++ wird ein lokales Objekt gelöscht, sobald es den Gültigkeitsbereich verlässt. In Java / C # ist das nicht wahr.
Jason Baker
Der Grund, warum Java und C # keine rechtzeitige Zerstörung garantieren können, liegt in der Möglichkeit von Referenzzyklen, was bedeutet, dass es unmöglich ist, eine sichere Reihenfolge für die Ausführung der Destruktoren zu bestimmen. Referenzzyklen können auch in C ++ auftreten, die Auswirkungen sind jedoch unterschiedlich: Der Programmierer ist für die Bestimmung der Vernichtungsreihenfolge und das Ausführen expliziter Löschvorgänge verantwortlich. Diese Verantwortung ist sehr oft in Destruktoren höherer Klassen zusammengefasst - z. B. ist eine Containerklasse dafür verantwortlich, dass alle enthaltenen Elemente zerstört werden. "Ownership" ist der Schlüssel.
Steve314
1
@Jason das habe ich mit "deterministische Zerstörung" gemeint - ein C ++ - Programmierer weiß, wann der Destruktor ausgeführt wird.
Kate Gregory
Ich weiß, dass dies eine alte Antwort ist, aber ich bin immer noch etwas verwirrt. Ich habe gerade von dem Begriff erfahren und einige Informationen besagen, dass die Erfassung im Konstruktor erfolgen sollte. Das ergibt für mich keinen Sinn und diese Antwort scheint dem zu widersprechen, aber könnten Sie das klarstellen?
Per Johansson
1
@PerJohansson Ja, du erwirbst im ctor. Und Sie geben im dtor frei. Ich habe mich auf den zweiten Punkt konzentriert, aber sie gehören zusammen. Sobald der Vorgang abgeschlossen ist, wissen Sie, dass Sie ein gültiges Objekt haben. Und Sie wissen, dass die Ressource, egal was passiert, zum richtigen Zeitpunkt freigegeben wird.
Kate Gregory
4

Bei RAII geht es zum Teil darum zu entscheiden, wann ein Objekt für seine eigene Bereinigung verantwortlich wird. Die Regel lautet, dass das Objekt verantwortlich wird, wenn die Konstruktorinitialisierung abgeschlossen ist. Die Symmetrie von Initialisierung und Bereinigung, Konstruktor und Destruktor, bedeutet, dass die beiden eng miteinander verbunden sind.

Ein Punkt von RAII ist die Gewährleistung der Ausnahmesicherheit - dass die Anwendung selbstkonsistent bleibt, wenn Ausnahmen ausgelöst werden. Auf den ersten Blick ist dies trivial - wenn eine Ausnahme das Verlassen eines Gültigkeitsbereichs verursacht, müssen die lokalen Variablen in diesem Gültigkeitsbereich zerstört werden.

Aber was passiert, wenn der Ausnahmewurf innerhalb eines Konstruktors auftritt?

Nun, das Objekt wurde noch nicht vollständig konstruiert und kann daher nicht sicher zerstört werden. Der Konstruktor sollte nach Bedarf über Try-Blöcke verfügen, um sicherzustellen, dass alle erforderlichen Bereinigungen durchgeführt werden, bevor die Ausnahme propogiert wird. Sobald die Ausnahme außerhalb des Bereichs auftritt, in dem das Objekt erstellt wurde, erfolgt kein Destruktoraufruf, da das Objekt nicht an erster Stelle erstellt wurde.

Berücksichtigen Sie insbesondere die Konstruktoren für Elementdaten innerhalb des zu zerstörenden Objekts. Wenn einer dieser Auslöser eine Ausnahme auslöst, wird Ihr Hauptkonstruktorcode überhaupt nicht ausgeführt, aber ein Code, der einen impliziten Teil dieses Konstruktors bildet, wird über Folgendes verfügen. Alle Mitglieder, die erfolgreich erstellt wurden, werden automatisch zerstört. Elemente, die nicht erstellt wurden (einschließlich desjenigen, der die Ausnahme ausgelöst hat), sind es nicht.

Grundsätzlich ist RAII eine Richtlinie, die sicherstellt, dass alles, was vollständig konstruiert wurde, rechtzeitig zerstört wird, insbesondere wenn Ausnahmewürfe vorliegen, und dass jedes Objekt vollständig konstruiert wird oder nicht (es gibt keine Halbbilder). konstruierte Objekte, die Sie nicht sicher bereinigen können). Zugewiesene Ressourcen werden ebenfalls freigegeben. Und ein Großteil der Arbeit ist automatisiert, sodass sich der Programmierer nicht zu viele Sorgen machen muss.

Steve314
quelle