Einleitung: Warum Fehlerbehandlung das Fundament zuverlässigen C++-Codes ist
Jedes Programm, das mit der Außenwelt interagiert (Dateien, Netzwerk, Benutzereingaben), stößt unweigerlich auf Ausnahmesituationen. In C++ ist der try/catch-Mechanismus das wichtigste Werkzeug für eine elegante und kontrollierte Handhabung solcher Situationen. In diesem Artikel analysieren wir die Fehlerbehandlung in C++ von den grundlegenden Konstrukten bis hin zu fortgeschrittenen Techniken, einschließlich Best Practices und Fallstricken.
1. Grundlagen: try, catch und throw
Der Ausnahmemechanismus in C++ basiert auf drei Schlüsselwörtern: try, catch und throw.
1.1. try- und catch-Blöcke
Der try-Block enthält Code, der eine Ausnahme auslösen kann. Der catch-Block fängt diese Ausnahme ab und behandelt sie.
#include <iostream>#include <stdexcept>
int main() { try { // Code, der einen Fehler verursachen kann int age = -5; if (age < 0) { throw std::invalid_argument("Alter kann nicht negativ sein"); } std::cout << "Alter: " << age << std::endl; } catch (const std::invalid_argument& e) { std::cerr << "Fehler: " << e.what() << std::endl; } return 0;}1.2. Ausnahmetypen
In C++ können Ausnahmen jeden Typs ausgelöst werden: Zahlen, Zeichenketten, Klassenobjekte. Es wird jedoch empfohlen, die Standard-Ausnahmeklassen aus <stdexcept> zu verwenden oder eigene Klassen zu erstellen, die von std::exception abgeleitet sind.
- Standard-Ausnahmen:
std::runtime_error,std::logic_error,std::out_of_rangeusw. - Benutzerdefinierte Ausnahmen: werden durch Vererbung von
std::exceptionerstellt.
class MyException : public std::exception {public: const char* what() const noexcept override { return "Meine eigene Ausnahme"; }};1.3. Mehrere catch-Blöcke
Sie können verschiedene Ausnahmetypen in separaten catch-Blöcken abfangen. Die Reihenfolge ist wichtig: spezifischere Ausnahmen sollten zuerst kommen.
try { // Code} catch (const std::invalid_argument& e) { // Behandlung von invalid_argument} catch (const std::exception& e) { // Behandlung aller anderen Ausnahmen aus std} catch (...) { // catch-all: fängt alles, was oben nicht gefangen wurde}2. Fortgeschrittene Techniken: Stack-Unwinding und RAII
2.1. Stack-Unwinding
Wenn eine Ausnahme ausgelöst wird, zerstört C++ automatisch alle lokalen Objekte auf dem Weg vom throw-Punkt zum entsprechenden catch. Dieser Vorgang wird als Stack-Unwinding bezeichnet. Er garantiert den Aufruf der Destruktoren für alle Objekte, die in try-Blöcken erstellt wurden.
class Resource {public: Resource() { std::cout << "Ressource erstellt"; } ~Resource() { std::cout << "Ressource zerstoert"; }};
void riskyFunction() { Resource res; throw std::runtime_error("Etwas ist schiefgelaufen");}
int main() { try { riskyFunction(); } catch (const std::exception& e) { std::cerr << e.what() << std::endl; } // Ausgabe: Ressource erstellt -> Ressource zerstoert -> Etwas ist schiefgelaufen}2.2. RAII (Resource Acquisition Is Initialization)
RAII ist eine zentrale C++-Idiom, die eng mit der Fehlerbehandlung verbunden ist. Die Idee: Die Verwaltung von Ressourcen (Speicher, Dateien, Mutexe) wird an die Lebensdauer eines Objekts gebunden. Der Destruktor gibt die Ressource automatisch frei, auch bei einer Ausnahme.
- Smart Pointer:
std::unique_ptr,std::shared_ptr