Menu
Coddy logo textTech

Benutzerdefinierte Ausnahmehierarchien

Teil des Abschnitts Objektorientierte Programmierung der C++-Journey von Coddy. Lektion 82 von 104.

Während die Standardbibliothek nützliche Ausnahme-Klassen wie std::runtime_error bereitstellt, profitieren Anwendungen aus der Praxis oft davon, benutzerdefinierte Ausnahmehierarchien zu erstellen. Indem du eigene Ausnahme-Klassen definierst, die von Standardausnahmen erben, kannst du domänenspezifische Fehlertypen erstellen, die zusätzlichen Kontext enthalten und eine präzisere Fehlerbehandlung ermöglichen.

Eine benutzerdefinierte Ausnahme kann typischerweise von std::exception oder einer ihrer abgeleiteten Klassen erben und die Methode what() überschreiben:

#include <exception>
#include <string>

class DatabaseException : public std::exception {
    std::string message;
public:
    DatabaseException(const std::string& msg) : message(msg) {}
    const char* what() const noexcept override { return message.c_str(); }
};

class ConnectionException : public DatabaseException {
public:
    ConnectionException(const std::string& host) 
        : DatabaseException("Failed to connect to: " + host) {}
};

class QueryException : public DatabaseException {
public:
    QueryException(const std::string& query) 
        : DatabaseException("Invalid query: " + query) {}
};

Diese Hierarchie ermöglicht es catch blocks, exceptions auf verschiedenen Ebenen der Spezifität zu behandeln:

try {
    // Datenbankoperationen...
    throw ConnectionException("localhost");
} catch (const ConnectionException& e) {
    std::cout << "Connection issue: " << e.what() << "\n";
} catch (const DatabaseException& e) {
    std::cout << "Database error: " << e.what() << "\n";
} catch (const std::exception& e) {
    std::cout << "General error: " << e.what() << "\n";
}

Der noexcept-Spezifizierer bei what() ist wichtig – er garantiert, dass die Methode keine Ausnahme auslöst, was von der Schnittstelle der Basisklasse verlangt wird. Ordne deine Catch-Blöcke vom spezifischsten zum allgemeinsten, da C++ den ersten passenden Handler verwendet.

challenge icon

Aufgabe

Einfach

Erstellen wir ein Dateiverarbeitungssystem mit einer benutzerdefinierten Ausnahmehierarchie, die verschiedene Arten dateibezogener Fehler behandelt. Du erstellst domänenspezifische Ausnahmen, die von einer grundlegenden Ausnahmeklasse erben und dadurch eine präzise Fehlerbehandlung auf verschiedenen Spezifitätsebenen ermöglichen.

Du organisierst deinen Code über drei Dateien:

  • FileExceptions.h: Definiere deine benutzerdefinierte Ausnahmehierarchie für Dateioperationen.

    Erstelle eine grundlegende FileException-Klasse, die von std::exception erbt. Sie sollte eine Fehlermeldung speichern und die Methode what() mit dem Spezifizierer noexcept überschreiben.

    Erstelle anschließend zwei abgeleitete Ausnahme-Klassen:

    • FileNotFoundException: nimmt einen Dateinamen entgegen und erstellt eine Meldung: File not found: [filename]
    • PermissionDeniedException: nimmt einen Dateinamen entgegen und erstellt eine Meldung: Permission denied: [filename]

    Jede abgeleitete Klasse sollte ihren übergeordneten Konstruktor mit der entsprechenden Meldung aufrufen.

  • FileProcessor.h: Definiere eine FileProcessor-Klasse, die Dateioperationen simuliert und deine benutzerdefinierten Ausnahmen auslöst.

    Dein FileProcessor sollte eine Methode openFile(const std::string& filename) besitzen, die das Öffnen einer Datei simuliert:

    • Wenn der Dateiname missing enthält, löse eine FileNotFoundException aus
    • Wenn der Dateiname locked enthält, löse eine PermissionDeniedException aus
    • Andernfalls gib den String Successfully opened: [filename] zurück
  • main.cpp: Lies einen einzelnen Dateinamen aus der Eingabe.

    Erstelle einen FileProcessor und versuche, die Datei zu öffnen. Verwende einen Try-Catch-Block mit Handlern, die von der spezifischsten zur allgemeinsten Ausnahme geordnet sind:

    1. Catch FileNotFoundException und gib aus: File Error: [message]
    2. Catch PermissionDeniedException und gib aus: Access Error: [message]
    3. Catch FileException und gib aus: General File Error: [message]

    Wenn keine Ausnahme ausgelöst wird, gib die von openFile() zurückgegebene Erfolgsmeldung aus.

Beispielsweise mit der Eingabe report.txt:

Successfully opened: report.txt

Mit der Eingabe missing_data.csv:

File Error: File not found: missing_data.csv

Mit der Eingabe locked_config.ini:

Access Error: Permission denied: locked_config.ini

Diese Herausforderung zeigt, wie du mit benutzerdefinierten Ausnahmehierarchien Fehler auf der jeweils passenden Detailebene behandeln kannst: spezifische Ausnahmen abfangen, wenn du eine präzise Behandlung benötigst, oder die grundlegende Ausnahme abfangen, wenn du alle Dateifehler einheitlich behandeln möchtest.

Probier es selbst

#include <iostream>
#include <string>
#include "FileExceptions.h"
#include "FileProcessor.h"

using namespace std;

int main() {
    string filename;
    cin >> filename;
    
    FileProcessor processor;
    
    // TODO: Verwende einen try-catch-Block, um Ausnahmen zu behandeln
    // Ordne die Handler vom spezifischsten zum allgemeinsten:
    // 1. Fange FileNotFoundException ab - gib aus: "File Error: [message]"
    // 2. Fange PermissionDeniedException ab - gib aus: "Access Error: [message]"
    // 3. Fange FileException ab - gib aus: "General File Error: [message]"
    // Wenn keine Ausnahme, gib die Erfolgsmeldung von openFile() aus
    
    try {
        // TODO: Rufe processor.openFile() auf und behandle das Ergebnis
        
    }
    // TODO: Füge catch-Blöcke in der Reihenfolge vom spezifischsten zum allgemeinsten hinzu
    
    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