Menu
Coddy logo textTech

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 freigegeben

Smart 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.

challenge icon

Aufgabe

Einfach

Erstellen 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 eine Connection-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, isOpen auf true setzt und "Opening connection: <id>" ausgibt
    • Einen Destruktor, der prüft, ob die Verbindung geöffnet ist, sie durch Setzen von isOpen auf 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
  • 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

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;
}
quiz iconTeste dich selbst

Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.

Alle Lektionen in Objektorientierte Programmierung

Übe selbstständig: Online-C++-Compiler