Es gibt ausgezeichnete Antworten, also füge ich nur einige vergessene Dinge hinzu.
0. Bei RAII geht es um Bereiche
Bei RAII geht es um beides:
- Erfassen einer Ressource (unabhängig von der Ressource) im Konstruktor und Aufheben der Erfassung im Destruktor.
- Der Konstruktor wird ausgeführt, wenn die Variable deklariert wird, und der Destruktor wird automatisch ausgeführt, wenn die Variable den Gültigkeitsbereich verlässt.
Andere haben bereits darauf geantwortet, deshalb werde ich nicht näher darauf eingehen.
1. Beim Codieren in Java oder C # verwenden Sie bereits RAII ...
MONSIEUR JOURDAIN: Was! Wenn ich sage: "Nicole, bring mir meine Hausschuhe und gib mir meinen Schlummertrunk", ist das Prosa?
PHILOSOPHY MASTER: Ja, Sir.
MONSIEUR JOURDAIN: Seit mehr als vierzig Jahren spreche ich Prosa, ohne etwas darüber zu wissen, und ich bin Ihnen sehr dankbar, dass Sie mir das beigebracht haben.
- Molière: Der Gentleman der Mittelklasse, Akt 2, Szene 4
Wie Monsieur Jourdain es mit Prosa tat, verwenden C # und sogar Java-Leute bereits RAII, aber auf versteckte Weise. Zum Beispiel der folgende Java-Code (der in C # durch Ersetzen synchronized
durch gleich geschrieben wird lock
):
void foo()
{
// etc.
synchronized(someObject)
{
// if something throws here, the lock on someObject will
// be unlocked
}
// etc.
}
... verwendet bereits RAII: Die Mutex-Erfassung erfolgt im Schlüsselwort ( synchronized
oder lock
), und die Aufhebung der Erfassung erfolgt beim Beenden des Bereichs.
Es ist so natürlich in seiner Notation, dass es selbst für Leute, die noch nie von RAII gehört haben, fast keiner Erklärung bedarf.
Der Vorteil von C ++ gegenüber Java und C # besteht darin, dass mit RAII alles möglich ist. Zum Beispiel gibt es kein direktes eingebautes Äquivalent von synchronized
oder lock
in C ++, aber wir können sie trotzdem haben.
In C ++ würde es geschrieben werden:
void foo()
{
// etc.
{
Lock lock(someObject) ; // lock is an object of type Lock whose
// constructor acquires a mutex on
// someObject and whose destructor will
// un-acquire it
// if something throws here, the lock on someObject will
// be unlocked
}
// etc.
}
die leicht auf Java / C # Weise geschrieben werden kann (unter Verwendung von C ++ - Makros):
void foo()
{
// etc.
LOCK(someObject)
{
// if something throws here, the lock on someObject will
// be unlocked
}
// etc.
}
2. RAII haben alternative Verwendungszwecke
WHITE RABBIT: [singend] Ich bin spät dran / ich bin spät dran / Für ein sehr wichtiges Date. / Keine Zeit "Hallo" zu sagen. / Auf Wiedersehen. / Ich bin spät, ich bin spät, ich bin spät.
- Alice im Wunderland (Disney-Version, 1951)
Sie wissen, wann der Konstruktor aufgerufen wird (bei der Objektdeklaration), und Sie wissen, wann der entsprechende Destruktor aufgerufen wird (am Ende des Bereichs), sodass Sie fast magischen Code mit nur einer Zeile schreiben können. Willkommen im C ++ - Wunderland (zumindest aus Sicht eines C ++ - Entwicklers).
Zum Beispiel können Sie ein Zählerobjekt schreiben (ich lasse das als Übung) und es verwenden, indem Sie einfach seine Variable deklarieren, wie das obige Sperrobjekt verwendet wurde:
void foo()
{
double timeElapsed = 0 ;
{
Counter counter(timeElapsed) ;
// do something lengthy
}
// now, the timeElapsed variable contain the time elapsed
// from the Counter's declaration till the scope exit
}
was natürlich wieder auf Java / C # Weise mit einem Makro geschrieben werden kann:
void foo()
{
double timeElapsed = 0 ;
COUNTER(timeElapsed)
{
// do something lengthy
}
// now, the timeElapsed variable contain the time elapsed
// from the Counter's declaration till the scope exit
}
3. Warum fehlt C ++ finally
?
[SCHREIEN] Es ist der letzte Countdown!
- Europa: Der letzte Countdown (Entschuldigung, ich hatte hier keine Zitate mehr ... :-)
Die finally
Klausel wird in C # / Java verwendet, um die Ressourcenentsorgung im Falle eines Bereichsausgangs zu handhaben (entweder durch eine return
oder eine ausgelöste Ausnahme).
Kluge Spezifikationsleser werden bemerkt haben, dass C ++ keine finally-Klausel hat. Und dies ist kein Fehler, da C ++ ihn nicht benötigt, da RAII bereits die Ressourcenentsorgung übernimmt. (Und glauben Sie mir, das Schreiben eines C ++ - Destruktors ist um ein Vielfaches einfacher als das Schreiben der richtigen Java finally-Klausel oder sogar der korrekten Dispose-Methode eines C #).
Trotzdem finally
wäre eine Klausel manchmal cool. Können wir das in C ++ machen? Ja wir können! Und wieder mit einer alternativen Verwendung von RAII.
Fazit: RAII ist in C ++ mehr als eine Philosophie: Es ist C ++
RAII? DAS IST C ++ !!!
- Der empörte Kommentar des C ++ - Entwicklers, schamlos kopiert von einem obskuren Sparta-König und seinen 300 Freunden
Wenn Sie ein gewisses Maß an Erfahrung in C ++ erreicht haben, denken Sie an RAII , an die automatisierte Ausführung von Construtoren und Destruktoren .
Sie fangen an, in Bereichen zu denken , und die Zeichen {
und }
werden zu einem der wichtigsten in Ihrem Code.
Und fast alles passt in Bezug auf RAII: Ausnahmesicherheit, Mutexe, Datenbankverbindungen, Datenbankanforderungen, Serververbindung, Uhren, Betriebssystemhandles usw. und nicht zuletzt Speicher.
Der Datenbankteil ist nicht zu vernachlässigen, da Sie, wenn Sie die Zahlung des Preises akzeptieren, sogar in einem " Transaktionsprogrammierungsstil " schreiben können , indem Sie Zeilen und Codezeilen ausführen, bis Sie am Ende entscheiden, ob Sie alle Änderungen übernehmen möchten oder, falls nicht möglich, alle Änderungen zurücksetzen lassen (solange jede Zeile mindestens die Garantie für starke Ausnahmen erfüllt). ( Informationen zur Transaktionsprogrammierung finden Sie im zweiten Teil dieses Herb's Sutter-Artikels .)
Und wie ein Puzzle passt alles.
RAII ist so sehr Teil von C ++, dass C ++ ohne C ++ nicht C ++ sein könnte.
Dies erklärt, warum erfahrene C ++ - Entwickler so verliebt in RAII sind und warum RAII das erste ist, wonach sie suchen, wenn sie eine andere Sprache ausprobieren.
Und es erklärt, warum der Garbage Collector, obwohl er an sich schon ein großartiges Stück Technologie ist, aus Sicht eines C ++ - Entwicklers nicht so beeindruckend ist:
- RAII behandelt bereits die meisten Fälle, die von einem GC bearbeitet werden
- Ein GC befasst sich besser als RAII mit Zirkelverweisen auf rein verwaltete Objekte (gemindert durch die intelligente Verwendung schwacher Zeiger).
- Ein GC ist jedoch auf den Speicher beschränkt, während RAII jede Art von Ressource verarbeiten kann.
- Wie oben beschrieben, kann RAII viel, viel mehr ...