Eigene Exception-Hierarchien
Teil des Abschnitts Objektorientierte Programmierung der C++-Journey von Coddy — Lektion 82 von 104.
Während die Standardbibliothek nützliche Ausnahmeklassen wie std::runtime_error bietet, profitieren Anwendungen in der Praxis oft von der Erstellung benutzerdefinierter Ausnahmehierarchien. Indem Sie Ihre eigenen Ausnahmeklassen definieren, die von Standardausnahmen erben, können Sie domänenspezifische Fehlertypen erstellen, die zusätzlichen Kontext enthalten und eine präzisere Fehlerbehandlung ermöglichen.
Eine benutzerdefinierte Exception-Klasse erbt normalerweise von std::exception oder einer ihrer abgeleiteten Klassen und überschreibt die what()-Methode:
#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-Blöcken, Ausnahmen auf verschiedenen Spezifitätsebenen 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 für what() ist wichtig – er garantiert, dass die Methode keine Exceptions wirft, was von der Schnittstelle der Basisklasse gefordert wird. Ordnen Sie Ihre catch-Blöcke vom Spezifischsten zum Allgemeinsten an, da C++ den ersten passenden Handler verwendet.
Aufgabe
EinfachLassen Sie uns ein Dateiverarbeitungssystem mit einer benutzerdefinierten Ausnahmehierarchie erstellen, die verschiedene Arten von dateibezogenen Fehlern verarbeitet. Sie werden domänenspezifische Ausnahmen erstellen, die von einer Basis-Ausnahmeklasse erben, was eine präzise Fehlerbehandlung auf verschiedenen Ebenen der Spezifität ermöglicht.
Sie werden Ihren Code über drei Dateien organisieren:
FileExceptions.h: Definieren Sie Ihre benutzerdefinierte Ausnahmehierarchie für Dateioperationen.Erstellen Sie eine Basisklasse
FileException, die vonstd::exceptionerbt. Sie sollte eine Fehlermeldung speichern und die Methodewhat()mit dem Spezifizierernoexceptüberschreiben.Erstellen Sie dann zwei abgeleitete Ausnahmeklassen:
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 Eltern-Konstruktor mit der entsprechenden Meldung aufrufen.
FileProcessor.h: Definieren Sie eine KlasseFileProcessor, die Dateioperationen simuliert und Ihre benutzerdefinierten Ausnahmen auslöst.Ihr
FileProcessorsollte eine MethodeopenFile(const std::string& filename)haben, die das Öffnen einer Datei simuliert:- Wenn der Dateiname
missingenthält, lösen Sie eineFileNotFoundExceptionaus - Wenn der Dateiname
lockedenthält, lösen Sie einePermissionDeniedExceptionaus - Andernfalls geben Sie den String
Successfully opened: [filename]zurück
- Wenn der Dateiname
main.cpp: Lesen Sie einen einzelnen Dateinamen von der Eingabe ein.Erstellen Sie einen
FileProcessorund versuchen Sie, die Datei zu öffnen. Verwenden Sie einen try-catch-Block mit Handlern, die von der spezifischsten zur allgemeinsten geordnet sind:- Fangen Sie
FileNotFoundExceptionab und geben Sie aus:File Error: [message] - Fangen Sie
PermissionDeniedExceptionab und geben Sie aus:Access Error: [message] - Fangen Sie
FileExceptionab und geben Sie aus:General File Error: [message]
Wenn keine Ausnahme ausgelöst wird, geben Sie die von
openFile()zurückgegebene Erfolgsmeldung aus.- Fangen Sie
Zum Beispiel bei der Eingabe report.txt:
Successfully opened: report.txtBei der Eingabe missing_data.csv:
File Error: File not found: missing_data.csvBei der Eingabe locked_config.ini:
Access Error: Permission denied: locked_config.iniDiese Herausforderung zeigt, wie benutzerdefinierte Ausnahmehierarchien es Ihnen ermöglichen, Fehler auf der angemessenen Detailebene zu behandeln – indem Sie spezifische Ausnahmen abfangen, wenn Sie eine präzise Handhabung benötigen, oder die Basisausnahme abfangen, wenn Sie alle Dateifehler einheitlich behandeln möchten.
Spickzettel
Benutzerdefinierte Exception-Klassen erben von std::exception oder deren abgeleiteten Klassen und überschreiben die Methode what():
#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(); }
};Erstellen Sie Exception-Hierarchien, indem Sie von benutzerdefinierten Basis-Exceptions erben:
class ConnectionException : public DatabaseException {
public:
ConnectionException(const std::string& host)
: DatabaseException("Failed to connect to: " + host) {}
};Behandeln Sie Exceptions auf verschiedenen Spezifitätsebenen, indem Sie Catch-Blöcke vom Spezifischsten zum Allgemeinsten ordnen:
try {
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 Bezeichner noexcept bei what() garantiert, dass die Methode keine Exception wirft, was von der Schnittstelle der Basisklasse gefordert wird.
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 Exceptions zu behandeln
// Ordne die Handler vom Spezifischsten zum Allgemeinsten:
// 1. Fange FileNotFoundException ab - Ausgabe: "File Error: [message]"
// 2. Fange PermissionDeniedException ab - Ausgabe: "Access Error: [message]"
// 3. Fange FileException ab - Ausgabe: "General File Error: [message]"
// Wenn keine Exception auftritt, gib die Erfolgsmeldung von openFile() aus
try {
// TODO: Rufe processor.openFile() auf und verarbeite das Ergebnis
}
// TODO: Füge catch-Blöcke in der Reihenfolge vom Spezifischsten zum Allgemeinsten hinzu
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 & QuelldateienNamensräume & GültigkeitsbereicheEinführung in OOP in C++Klassen vs. ObjekteDer 'this'-PointerMethoden (Elementfunktionen)Attribute (Datenelemente)Grundlagen zu Ctors & DtorsRückblick - Einfacher Taschenrechner4Klasseneigenschaften
Instanz- vs. statische MemberGetter und SetterConst-MemberfunktionenMutable-SchlüsselwortStatische Methoden und VariablenFriend-Funktionen & KlassenZusammenfassung - Bankkonto-Manager7Vererbung
Grundlagen der VererbungZugriffsstufen bei VererbungAufrufreihenfolge von Ctor & DtorÜberschreiben von MethodenVirtuelle Funktionen & VTableMehrfachvererbungVirtuelle VererbungRückblick - Mitarbeiter-Hierarchie2Speicherverwaltung
Stack vs. Heap SpeicherZeiger und ReferenzenDynamischer Speicher (new/delete)Smart Pointers in C++RAII in C++Rückblick – Dynamischer Array-Manager5Kapselung
Zugriffsspezifizierer in C++Zugriffsspezifizierer im DetailInformation HidingStruct vs. ClassVerschachtelte & innere KlassenRückblick - Studentenverwaltungssystem8Polymorphie
Kompilierzeit- vs. Laufzeit-PolymorphieFunktionsüberladungVirtuelle Funktionen – WiederholungRein virtuelle FunktionenAbstrakte KlassenInterface-Design in C++Dynamic Casting & RTTIZusammenfassung – Formen-Rechner11Fortgeschrittene OOP-Konzepte
Komposition vs. VererbungMixins via CRTPPimpl-IdiomType ErasureEnum-Klassen & starke TypisierungException-Handling in OOPEigene Exception-Hierarchien3Konstruktoren & Destruktoren
StandardkonstruktorParametrisierter KonstruktorKopierkonstruktorVerschiebekonstruktorKonstruktor-InitialisierungslistenDelegierende KonstruktorenDestruktoren im DetailRule of Three / Five / ZeroZusammenfassung - String-Klasse6Operatorüberladung
Einführung in die OperatorüberladungArithmetische OperatorüberladungVergleichsoperatorüberladungStream-OperatorenZuweisungsoperatorüberladungÜberladen der [] und () OperatorenTypumwandlungsoperatorenRückblick - Matrix-Klasse9Templates
FunktionstemplatesKlassentemplatesTemplate-SpezialisierungVariadische TemplatesSFINAE & Type Traits GrundlagenRückblick - Generische Container