Menu
Coddy logo textTech

Ausnahmebehandlung in OOP

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

Exception-Behandlung in C++ bietet eine strukturierte Möglichkeit, Laufzeitfehler zu erkennen und darauf zu reagieren. In der objektorientierten Programmierung fügen sich Exceptions natürlich in Klassenhierarchien ein, sodass du aussagekräftige Fehlertypen erstellen kannst, die Kontext darüber enthalten, was schiefgelaufen ist.

Der grundlegende Mechanismus verwendet drei Schlüsselwörter: try, throw und catch. Code, der möglicherweise fehlschlägt, kommt in einen try-Block, Fehler werden mit throw signalisiert, und catch-Blöcke behandeln bestimmte Ausnahmetypen:

#include <iostream>
#include <stdexcept>

class BankAccount {
    double balance;
public:
    BankAccount(double b) : balance(b) {}
    
    void withdraw(double amount) {
        if (amount > balance) {
            throw std::runtime_error("Insufficient funds");
        }
        balance -= amount;
    }
    
    double getBalance() const { return balance; }
};

int main() {
    BankAccount account(100.0);
    
    try {
        account.withdraw(150.0);
    } catch (const std::runtime_error& e) {
        std::cout << "Error: " << e.what() << "\n";
    }
    
    std::cout << "Balance: " << account.getBalance() << "\n";
}

Die C++-Standardbibliothek stellt eine Hierarchie von Ausnahme-Klassen bereit, deren Wurzel std::exception ist. Zu den häufigen abgeleiteten Klassen gehören std::runtime_error für Laufzeitprobleme und std::logic_error für Programmierfehler. Alle Standardausnahmen stellen eine what()-Methode bereit, die eine Fehlermeldung zurückgibt.

Wenn eine exception ausgelöst wird, wird der Stack abgewickelt – Destruktoren werden für alle lokalen Objekte aufgerufen, während das Programm nach einem passenden catch-Block sucht. Dadurch wird eine ordnungsgemäße Bereinigung auch dann sichergestellt, wenn Fehler auftreten. Deshalb funktionieren RAII und die Ausnahmebehandlung so gut zusammen.

challenge icon

Aufgabe

Einfach

Erstellen wir einen sicheren Divisionsrechner, der die Ausnahmebehandlung in einem objektorientierten Kontext demonstriert. Du erstellst eine Calculator-Klasse, die Divisionsoperationen ausführt und aussagekräftige Ausnahmen auslöst, wenn etwas schiefgeht, und behandelst diese Ausnahmen anschließend in deinem Hauptprogramm auf geeignete Weise.

Du organisierst deinen Code auf drei Dateien verteilt:

  • Calculator.h: Definiere deine Calculator-Klasse, die mathematische Operationen mit geeigneter Fehlerbehandlung ausführt.

    Dein Calculator sollte eine Methode divide(double numerator, double denominator) besitzen, die das Ergebnis der Division zurückgibt. Wenn der Nenner jedoch null ist, sollte sie eine std::runtime_error mit der Nachricht Division by zero auslösen. Wenn entweder der Zähler oder der Nenner negativ ist, sollte sie eine std::invalid_argument mit der Nachricht Negative values not allowed auslösen.

    Füge außerdem eine Methode safeSqrt(double value) hinzu, die die Quadratwurzel des Werts zurückgibt. Wenn der Wert negativ ist, löse eine std::runtime_error mit der Nachricht Cannot compute square root of negative number aus. Du musst <cmath> für die Funktion sqrt einbinden.

  • Calculator.cpp: Implementiere die in deiner Header-Datei deklarierten Methoden. Achte darauf, die erforderlichen Header (<stdexcept> und <cmath>) einzubinden und die Bedingungen vor der Ausführung der Operationen zu prüfen.
  • main.cpp: Lies drei Eingaben ein (jede in einer eigenen Zeile):
    1. Operationstyp: entweder divide oder sqrt
    2. Erste Zahl (double)
    3. Zweite Zahl (double): wird nur für die Division verwendet, für sqrt ignoriert

    Erstelle ein Calculator-Objekt und führe die angeforderte Operation innerhalb eines try-Blocks aus. Wenn die Operation erfolgreich ist, gib Result: [value] aus. Wenn eine Ausnahme abgefangen wird, gib Error: [message] aus, wobei die Nachricht aus der what()-Methode der Ausnahme stammt.

    Gib nach dem try-catch-Block (unabhängig davon, ob eine Ausnahme aufgetreten ist) Calculation complete aus, um zu zeigen, dass das Programm nach der Behandlung von Ausnahmen weiter ausgeführt wird.

Beispielsweise mit den Eingaben divide, 10 und 2:

Result: 5
Calculation complete

Mit den Eingaben divide, 10 und 0:

Error: Division by zero
Calculation complete

Mit den Eingaben divide, -5 und 2:

Error: Negative values not allowed
Calculation complete

Mit den Eingaben sqrt, 16 und 0:

Result: 4
Calculation complete

Mit den Eingaben sqrt, -9 und 0:

Error: Cannot compute square root of negative number
Calculation complete

Beachte, wie das Programm Fehler auf geeignete Weise behandelt, indem es Ausnahmen abfängt und aussagekräftige Nachrichten anzeigt, und anschließend die Ausführung fortsetzt. Dieses Muster hält deine Fehlerbehandlung übersichtlich und deine Hauptlogik auf den normalen Ablauf konzentriert.

Probier es selbst

#include <iostream>
#include <string>
#include "Calculator.h"

using namespace std;

int main() {
    // Eingaben lesen
    string operation;
    double num1, num2;
    
    cin >> operation;
    cin >> num1;
    cin >> num2;
    
    // Calculator-Objekt erstellen
    Calculator calc;
    
    // TODO: Verwende einen try-catch-Block, um die Operation durchzuführen
    // - Wenn operation "divide" ist, rufe calc.divide(num1, num2) auf
    // - Wenn operation "sqrt" ist, rufe calc.safeSqrt(num1) auf
    // - Bei Erfolg gib "Result: " gefolgt vom Ergebnis aus
    // - Fange Ausnahmen ab und gib "Error: " gefolgt von der Ausnahme-Nachricht aus (verwende what())
    // - After the try-catch, always print "Calculation complete"
    
    
    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