C++ Ausnahmebehandlung

Hallo dort, aufstrebende Programmierer! Heute werden wir eine spannende Reise in die Welt der C++ Ausnahmebehandlung antreten. Als euer freundlicher Nachbar und Informatiklehrer bin ich hier, um euch durch dieses wichtige Thema zu führen. Also, holt euch eure Lieblingsgetränke, macht euch komfortabel und lasst uns einsteigen!

C++ Exception Handling

Was sind Ausnahmen?

Bevor wir wie erfahrene Jonglierer Ausnahmen werfen und fangen, lassen uns erstmal verstehen, was sie sind. In der Programmierwelt sind Ausnahmen unerwartete Ereignisse, die während der Ausführung eines Programms auftreten. Sie sind wie die überraschenden Pop-Quizzes, die ich früher gab (sorry dafür!) – unerwartet und manchmal etwas schwierig zu handhaben.

Ausnahmen stören den normalen Ablauf der Programmanweisungen. Sie können durch verschiedene Faktoren verursacht werden, wie z.B.:

  1. Division durch null
  2. Zugriff auf ein Array außerhalb der Grenzen
  3. Ausgabe von Speicher
  4. Versuch, eine Datei zu öffnen, die nicht existiert

Nun lassen uns sehen, wie C++ es uns ermöglicht, diese unerwarteten Situationen elegant zu verwalten.

Ausnahmen werfen

Die Grundlagen des Wurfens

In C++ verwenden wir das Schlüsselwort throw, um eine Ausnahme zu erzeugen. Es ist wie das Heben eurer Hand in der Klasse, wenn ihr eine Frage oder ein Problem habt. Hier ist ein einfaches Beispiel:

#include <iostream>
using namespace std;

int main() {
try {
throw 20;
}
catch (int e) {
cout << "Eine Ausnahme ist aufgetreten. Ausnahme Nr. " << e << endl;
}
return 0;
}

In diesem Beispiel werfen wir eine Ganzzahl-Ausnahme mit dem Wert 20. Aber keine Sorge, wir werden sie gleich fangen!

Verschiedene Typen werfen

C++ ist flexibel und ermöglicht es euch, Ausnahmen verschiedener Typen zu werfen. Lassen uns ein praktischeres Beispiel betrachten:

#include <iostream>
#include <stdexcept>
using namespace std;

double teile(int a, int b) {
if (b == 0) {
throw runtime_error("Division durch null!");
}
return static_cast<double>(a) / b;
}

int main() {
try {
cout << teile(10, 2) << endl;  // Dies wird ohne Probleme funktionieren
cout << teile(10, 0) << endl;  // Dies wird eine Ausnahme werfen
}
catch (const runtime_error& e) {
cout << "Gefangene Ausnahme: " << e.what() << endl;
}
return 0;
}

In diesem Beispiel werfen wir eine runtime_error-Ausnahme, wenn jemand versucht, durch null zu teilen. Es ist wie das Aufhängen eines "Keine Division durch null" Schildes in unserer mathematischen Nachbarschaft!

Ausnahmen fangen

Die Grundlagen des Fangens

Nun, da wir wissen, wie man Ausnahmen wirft, lernen wir, wie man sie fängt. Ausnahmebehandlung ist wie ein verantwortungsbewusster Haustierbesitzer – ihr müsst bereit sein, alles zu handhaben, was euer Code wirft!

Wir verwenden einen try-catch-Block, um Ausnahmen zu fangen. Der try-Block enthält den Code, der möglicherweise eine Ausnahme wirft, und der catch-Block behandelt die Ausnahme, wenn sie auftritt.

#include <iostream>
using namespace std;

int main() {
try {
int alter = -5;
if (alter < 0) {
throw "Alter kann nicht negativ sein!";
}
cout << "Alter ist: " << alter << endl;
}
catch (const char* msg) {
cerr << "Fehler: " << msg << endl;
}
return 0;
}

In diesem Beispiel überprüfen wir, ob das Alter negativ ist. Wenn ja, werfen wir eine Ausnahme mit einer benutzerdefinierten Fehlermeldung.

Mehrere Ausnahmen fangen

Manchmal können verschiedene Arten von Ausnahmen aus dem gleichen Codeabschnitt geworfen werden. In solchen Fällen können wir mehrere catch-Blöcke haben:

#include <iostream>
#include <stdexcept>
using namespace std;

int main() {
try {
int wahl;
cout << "Geben Sie 1 für Ganzzahl-Ausnahme, 2 für Laufzeitfehler ein: ";
cin >> wahl;

if (wahl == 1) {
throw 42;
} else if (wahl == 2) {
throw runtime_error("Ein wilder Laufzeitfehler ist aufgetreten!");
} else {
throw "Unbekannte Wahl!";
}
}
catch (int e) {
cout << "Gefangene Ganzzahl-Ausnahme: " << e << endl;
}
catch (const runtime_error& e) {
cout << "Gefangener Laufzeitfehler: " << e.what() << endl;
}
catch (...) {
cout << "Gefangene unbekannte Ausnahme!" << endl;
}
return 0;
}

Dieses Beispiel zeigt, wie wir verschiedene Arten von Ausnahmen fangen können. Der catch (...)-Block ist ein Fang-all, der jede Ausnahme behandelt, die nicht von den vorherigen catch-Blöcken gefangen wird. Es ist wie eine Sicherheitsnetz für alle diese unerwarteten Überraschungen!

C++ Standardausnahmen

C++ kommt mit einer Reihe von Standardausnahmen, die ihr in euren Programmen verwenden könnt. Diese sind wie die Schweizer Armymesser der Ausnahme Welt – vielseitig und immer bereit, zu helfen!

Hier ist eine Tabelle einiger häufig verwendeter Standardausnahmen:

Ausnahme Beschreibung
std::runtime_error Laufzeitlogikfehler
std::logic_error Logikfehler
std::out_of_range Zugriff jenseits des Bereichs
std::overflow_error Arithmetischer Überlauf
std::bad_alloc Speicherzuweisung schlägt fehl

Lassen uns ein Beispiel mit einer Standardausnahme sehen:

#include <iostream>
#include <vector>
#include <stdexcept>
using namespace std;

int main() {
vector<int> zahlen = {1, 2, 3, 4, 5};
try {
cout << zahlen.at(10) << endl;  // Dies wird eine out_of_range-Ausnahme werfen
}
catch (const out_of_range& e) {
cerr << "Ausserhalb des Bereichs Fehler: " << e.what() << endl;
}
return 0;
}

In diesem Beispiel versuchen wir, auf ein Element zuzugreifen, das außerhalb des Bereichs unseres Vektors liegt. Die at()-Funktion wirft eine out_of_range-Ausnahme, wenn dies passiert.

Neue Ausnahmen definieren

Während die Standardausnahmen großartig sind, braucht man manchmal etwas spezifischer für seine spezifischen Bedürfnisse. Hier kommen benutzerdefinierte Ausnahmen ins Spiel!

So könnt ihr eine eigene Ausnahme Klasse definieren:

#include <iostream>
#include <exception>
using namespace std;

class NegativeValueException : public exception {
public:
const char* what() const throw() {
return "Negative Werte sind nicht erlaubt!";
}
};

double wurzel(double x) {
if (x < 0) {
throw NegativeValueException();
}
return sqrt(x);
}

int main() {
try {
cout << wurzel(25) << endl;  // Dies wird ohne Probleme funktionieren
cout << wurzel(-5) << endl;  // Dies wird unsere benutzerdefinierte Ausnahme werfen
}
catch (const NegativeValueException& e) {
cerr << "Fehler: " << e.what() << endl;
}
return 0;
}

In diesem Beispiel haben wir eine benutzerdefinierte NegativeValueException-Klasse erstellt. Wir verwenden sie in unserer wurzel-Funktion, um eine Ausnahme zu werfen, wenn jemand versucht, die Wurzel einer negativen Zahl zu berechnen.

Und das ist es, Leute! Wir haben die Grundlagen der C++ Ausnahmebehandlung abgedeckt. Denkt daran, Ausnahmen sind eure Freunde. Sie helfen euch, robuster und fehlerresistenter Code zu schreiben. Übung macht den Meister, und bald werdet ihr Ausnahmen wie ein Profi fangen!

Happy coding, und möge eure Ausnahmen immer gefangen werden!

Credits: Image by storyset