Fehlerbehandlung in C++: try, catch und moderne Ansätze

Online Python-Trainer für Anfänger

Lernen Sie Python einfach ohne theoretische Überlastung. Lösen Sie praktische Aufgaben mit automatischer Überprüfung und schreiben Sie Code direkt im Browser.

Kurs starten

Fehlerbehandlung in C++: von try/catch bis RAII und noexcept



Die Zuverlässigkeit eines Programms hängt direkt davon ab, wie es auf unvorhergesehene Situationen reagiert. In C++ sind Ausnahmen der primäre Mechanismus dafür. In diesem Artikel analysieren wir die klassische Konstruktion try/catch/throw und betrachten auch alternative und moderne Ansätze: RAII, noexcept und Fehlerbehandlung ohne Ausnahmen.



1. Basismechanismus: try, catch, throw



Eine Ausnahme ist ein Objekt, das beim Auftreten eines Fehlers "geworfen" (throw) wird. Das Programm kann es abfangen (catch) und korrigierende Maßnahmen durchführen, ohne die Arbeit vollständig zu unterbrechen.



Syntax

#include <iostream>#include <stdexcept>

double divide(double a, double b) { if (b == 0.0) { throw std::runtime_error("Division durch Null!"); } return a / b;}

int main() { try { double result = divide(10.0, 0.0); std::cout << "Ergebnis: " << result << std::endl; } catch (const std::runtime_error& e) { std::cerr << "Fehler: " << e.what() << std::endl; } return 0;}


Arbeitsregeln

  • throw — erstellt eine Kopie des Objekts und übergibt die Kontrolle an den Handler.
  • try — Codeblock, in dem Ausnahmen auftreten können.
  • catch — fängt die Ausnahme nach Typ ab. Es können mehrere catch-Blöcke angegeben werden.
  • Wenn eine Ausnahme nicht abgefangen wird, wird std::terminate() aufgerufen.


Abfangen nach Basisklasse

Alle Standardausnahmen erben von std::exception. Daher kann man einen universellen Handler schreiben:

try {    // Code} catch (const std::exception& e) {    std::cerr << "Standardfehler: " << e.what() << std::endl;} catch (...) {    std::cerr << "Unbekannte Ausnahme" << std::endl;}

Der Block catch (...) fängt grundsätzlich alle Ausnahmen, jedoch ohne Zugriff auf das Objekt.



2. Ausnahmen in Konstruktoren und Destruktoren



Konstruktoren

Wenn ein Konstruktor eine Ausnahme wirft, gilt das Objekt als nicht erstellt, und der Destruktor wird für es nicht aufgerufen. Dies ist wichtig für die Ressourcenverwaltung. Beispiel:

class Resource {public:    Resource() {        std::cout << "Ressource zugewiesen" << std::endl;        throw std::runtime_error("Initialisierungsfehler");    }    ~Resource() {        std::cout << "Ressource freigegeben" << std::endl;    }};

int main() { try { Resource r; } catch (...) { std::cout << "Ausnahme abgefangen" << std::endl; } // Der Destruktor von Resource wird NICHT aufgerufen! return 0;}


Destruktoren

Ausnahmen aus einem Destruktor zu werfen ist schlechte Praxis. Wenn der Destruktor während der Stack-Abwicklung (stack unwinding) aufgerufen wird und er eine neue Ausnahme wirft, wird das Programm abrupt beendet. Schreiben Sie Destruktoren immer als noexcept (standardmäßig sind sie das bereits).



3. RAII — Automatische Ressourcenverwaltung

Die Idiom RAII (Resource Acquisition Is Initialization) ist der Schlüssel zu sicherem Code. Eine Ressource (Speicher, Datei, Mutex) wird im Konstruktor erfasst und im Destruktor freigegeben. Wenn eine Ausnahme auftritt, werden die Destruktoren aller erstellten Objekte automatisch aufgerufen (stack unwinding), und die Ressourcen werden freigegeben.



Beispiel mit dem intelligenten Zeiger std::unique_ptr:

#include <memory>#include <iostream>

void process() { auto ptr = std::make_unique<int>(42); // Wenn hier eine Ausnahme auftritt, // gibt unique_ptr den Speicher automatisch frei throw std::runtime_error("Fehler in proc");

Blogs

Buchempfehlungen