RAII in C++
Teil des Abschnitts Objektorientierte Programmierung der C++-Journey von Coddy. Lektion 16 von 104.
RAII (Resource Acquisition Is Initialization) ist ein grundlegendes C++-Idiom, bei dem die Ressourcenverwaltung an die Lebensdauer eines Objekts gebunden ist. Die Idee ist einfach: Ressourcen im Konstruktor erwerben und sie im Destruktor freigeben.
Wenn ein Objekt erstellt wird, erwirbt es alle Ressourcen, die es benötigt. Wenn das Objekt den Gültigkeitsbereich verlässt, gibt sein Destruktor diese Ressourcen automatisch frei. Dies gewährleistet die Bereinigung, selbst wenn Ausnahmen auftreten.
class FileHandler {
std::FILE* file;
public:
FileHandler(const char* filename) {
file = std::fopen(filename, "r"); // Ressource erwerben
}
~FileHandler() {
if (file) std::fclose(file); // Ressource freigeben
}
};Mit RAII vergisst du nie das Aufräumen:
void processFile() {
FileHandler handler("data.txt"); // Ressource erworben
// Mit Datei arbeiten...
// Wenn hier eine Exception geworfen wird, läuft der Destruktor trotzdem!
} // Ressource wird automatisch freigegebenSmart Pointer sind das perfekte Beispiel für RAII in der Praxis. Ein unique_ptr übernimmt in seinem Konstruktor den Besitz des Heapspeichers und gibt ihn in seinem Destruktor frei. Du hast RAII bereits verwendet, ohne es zu merken!
RAII gilt für jede Ressource: Speicher, Dateihandles, Netzwerkverbindungen, Mutexes oder Datenbankverbindungen. Indem Ressourcen in Objekte eingeschlossen werden, gewährleistet C++ eine deterministische Bereinigung und beseitigt ganze Fehlerkategorien wie Leaks und verwaiste Handles.
Aufgabe
EinfachErstellen wir einen Verbindungsmanager, der RAII demonstriert, indem er den Lebenszyklus von netzwerkähnlichen Verbindungen automatisch verwaltet. Wenn eine Verbindung erstellt wird, sollte sie sich „öffnen“, und wenn sie ihren Gültigkeitsbereich verlässt, sollte sie sich automatisch „schließen“: keine manuelle Bereinigung erforderlich!
Du wirst zwei Dateien erstellen, um deinen Code zu organisieren:
Connection.h: Definiere eineConnection-Klasse, die eine Verbindungsressource nach den RAII-Prinzipien verwaltet. Die Klasse sollte Folgendes enthalten:- Ein privates Attribut
connectionId(String), um die Verbindung zu identifizieren - Ein privates Attribut
isOpen(Bool), um den Verbindungsstatus zu verfolgen - Einen Konstruktor, der eine Verbindungs-ID entgegennimmt,
isOpenauf true setzt und"Opening connection: <id>"ausgibt - Einen Destruktor, der prüft, ob die Verbindung geöffnet ist, sie durch Setzen von
isOpenauf false schließt und"Closing connection: <id>"ausgibt - Eine
send(string message)-Methode, die"[<id>] Sending: <message>"ausgibt, wenn die Verbindung geöffnet ist - Eine
getId()-Methode, die die Verbindungs-ID zurückgibt
- Ein privates Attribut
main.cpp: Demonstriere, wie RAII eine automatische Bereinigung sicherstellt, indem eine Verbindungs-ID und eine Nachricht aus der Eingabe gelesen werden. Anschließend:- Erstelle mit geschweiften Klammern
{ }einen Gültigkeitsbereich - Erstelle innerhalb dieses Gültigkeitsbereichs ein
Connection-Objekt auf dem Stack mit der eingegebenen ID - Verwende die Verbindung, um die eingegebene Nachricht zu senden
- Lass den Gültigkeitsbereich enden (die Verbindung sollte automatisch geschlossen werden)
- Gib nach dem Gültigkeitsbereich
"Connection out of scope"aus
- Erstelle mit geschweiften Klammern
Das Schöne an RAII ist, dass du dich nicht daran erinnern musst, die Verbindung zu schließen: Der Destruktor übernimmt dies automatisch, wenn das Objekt den Gültigkeitsbereich verlässt. Dieses Muster verhindert Ressourcenlecks, selbst wenn in deinem Code etwas Unerwartetes passiert.
Binde deine Header-Datei in main.cpp mit #include "Connection.h" ein.
Probier es selbst
#include <iostream>
#include <string>
#include "Connection.h"
using namespace std;
int main() {
string connectionId;
string message;
// Eingabe lesen
cin >> connectionId;
cin.ignore();
getline(cin, message);
// TODO: Erstelle einen Gültigkeitsbereich mit geschweiften Klammern { }
// Innerhalb des Gültigkeitsbereichs:
// - Erstelle ein Connection-Objekt auf dem Stack mit connectionId
// - Verwende die Connection, um die Nachricht zu senden
// Lass den Gültigkeitsbereich enden (Destruktor wird automatisch aufgerufen)
// TODO: After the scope ends, print "Connection out of scope"
return 0;
}
Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.
Alle Lektionen in Objektorientierte Programmierung
1Grundlagen der OOP
Externe DateienC++ Build & KompilierungHeader-Dateien & QuelldateienNamespaces & GültigkeitsbereichEinführung in OOP mit C++Klassen vs. ObjekteDer „this“-ZeigerMethoden (Member-Funktionen)Attribute (Datenmember)Grundlagen zu Ctors & DtorsRückblick – Einfacher Taschenrechner4Klassenattribute
Instanz- vs. statische MemberGetter und SetterConst-MemberfunktionenMutable-SchlüsselwortStatische Methoden und VariablenFriend-Funktionen und -KlassenZusammenfassung – Bankkontoverwaltung7Vererbung
Grundlagen der VererbungZugriffsebenen bei der VererbungAufrufreihenfolge von Ctor & DtorÜberschreiben von MethodenVirtuelle Funktionen & VTableMehrfachvererbungVirtuelle VererbungRückblick – Mitarbeiterhierarchie2Speicherverwaltung
Stack- vs. Heap-SpeicherZeiger und ReferenzenDynamischer Speicher (new/delete)Intelligente Zeiger in C++RAII in C++Rückblick – Dynamischer Array-Manager5Kapselung
Zugriffsbezeichner in C++Zugriffsbezeichner im DetailInformationsverbergungStruct vs. KlasseVerschachtelte und innere KlassenWiederholung – Studierendenverwaltungssystem8Polymorphie
Polymorphie zur Compilezeit vs. LaufzeitFunktionsüberladungVirtuelle Funktionen erneut betrachtetRein virtuelle FunktionenAbstrakte KlassenInterface-Design in C++Dynamic Casting & RTTIRückblick – Shape-Calculator3Konstruktoren & Destruktoren
StandardkonstruktorParametrisierter KonstruktorKopierkonstruktorMove-KonstruktorKonstruktor-InitialisierungslistenDelegierende KonstruktorenDestruktor im DetailDrei-/Fünf-/Null-RegelZusammenfassung – String-Klasse6Operatorüberladung
Einführung in die OperatorüberladungArithmetische OperatorüberladungVergleichsoperatorüberladungStream-OperatorenZuweisungsoperatorüberladungÜberladung der Operatoren [] und ()TypkonvertierungsoperatorenWiederholung – Matrix-Klasse9Templates
FunktionstemplatesKlassen-TemplatesTemplate-SpezialisierungVariadische TemplatesGrundlagen zu SFINAE und Type TraitsRückblick – Generischer ContainerÜbe selbstständig: Online-C++-Compiler