Menu
Coddy logo textTech

Obsługa wyjątków w OOP

Część sekcji Programowanie obiektowe ścieżki C++ w Coddy. Lekcja 81 z 104.

Obsługa wyjątków w C++ zapewnia uporządkowany sposób wykrywania błędów czasu wykonania i reagowania na nie. W programowaniu obiektowym wyjątki naturalnie integrują się z hierarchiami klas, umożliwiając tworzenie znaczących typów błędów, które zawierają kontekst dotyczący tego, co poszło nie tak.

Podstawowy mechanizm wykorzystuje trzy słowa kluczowe: try, throw i catch. Kod, który może zawieść, umieszcza się w bloku try, błędy sygnalizuje się za pomocą throw, a bloki catch obsługują określone typy wyjątków:

#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";
}

Biblioteka standardowa C++ udostępnia hierarchię klas wyjątków, której podstawą jest std::exception. Do często używanych klas pochodnych należą std::runtime_error, służąca do obsługi problemów występujących w czasie działania, oraz std::logic_error, służąca do obsługi błędów programistycznych. Wszystkie standardowe wyjątki udostępniają metodę what(), która zwraca komunikat o błędzie.

Gdy zostaje zgłoszony wyjątek, stos jest rozwijany — destruktory są wywoływane dla wszystkich obiektów lokalnych, gdy program szuka pasującego bloku catch. Zapewnia to prawidłowe sprzątanie nawet w przypadku wystąpienia błędów, dlatego RAII i obsługa wyjątków tak dobrze ze sobą współpracują.

challenge icon

Wyzwanie

Łatwy

Zbudujmy bezpieczny kalkulator dzielenia, który demonstruje obsługę wyjątków w kontekście obiektowym. Utworzysz klasę Calculator, która wykonuje operacje dzielenia i zgłasza odpowiednie wyjątki, gdy coś pójdzie nie tak, a następnie prawidłowo obsłużysz te wyjątki w programie głównym.

Podzielisz kod na trzy pliki:

  • Calculator.h: Zdefiniuj klasę Calculator, która wykonuje operacje matematyczne z odpowiednią obsługą błędów.

    Twoja klasa Calculator powinna mieć metodę divide(double numerator, double denominator), która zwraca wynik dzielenia. Jeśli jednak mianownik wynosi zero, metoda powinna zgłosić wyjątek std::runtime_error z komunikatem Division by zero. Jeśli licznik lub mianownik jest ujemny, powinna zgłosić wyjątek std::invalid_argument z komunikatem Negative values not allowed.

    Dodaj też metodę safeSqrt(double value), która zwraca pierwiastek kwadratowy z wartości. Jeśli wartość jest ujemna, zgłoś wyjątek std::runtime_error z komunikatem Cannot compute square root of negative number. Musisz dołączyć <cmath>, aby użyć funkcji sqrt.

  • Calculator.cpp: Zaimplementuj metody zadeklarowane w pliku nagłówkowym. Pamiętaj, aby dołączyć wymagane nagłówki (<stdexcept> i <cmath>) oraz sprawdzić warunki przed wykonaniem operacji.
  • main.cpp: Wczytaj trzy dane wejściowe (każdą w osobnym wierszu):
    1. Typ operacji: divide lub sqrt
    2. Pierwsza liczba (double)
    3. Druga liczba (double): używana tylko przy dzieleniu, ignorowana przy obliczaniu pierwiastka

    Utwórz obiekt Calculator i wykonaj żądaną operację wewnątrz bloku try. Jeśli operacja się powiedzie, wypisz Result: [value]. Jeśli zostanie przechwycony wyjątek, wypisz Error: [message], gdzie komunikat pochodzi z metody wyjątku what().

    Po bloku try-catch (niezależnie od tego, czy wystąpił wyjątek) wypisz Calculation complete, aby pokazać, że program kontynuuje działanie po obsłużeniu wyjątków.

Na przykład, dla danych wejściowych divide, 10 i 2:

Result: 5
Calculation complete

Dla danych wejściowych divide, 10 i 0:

Error: Division by zero
Calculation complete

Dla danych wejściowych divide, -5 i 2:

Error: Negative values not allowed
Calculation complete

Dla danych wejściowych sqrt, 16 i 0:

Result: 4
Calculation complete

Dla danych wejściowych sqrt, -9 i 0:

Error: Cannot compute square root of negative number
Calculation complete

Zwróć uwagę, jak program prawidłowo obsługuje błędy, przechwytując wyjątki i wyświetlając zrozumiałe komunikaty, a następnie kontynuuje działanie. Ten wzorzec pozwala zachować przejrzystą obsługę błędów i skupić główną logikę programu na typowym przebiegu działania.

Spróbuj swoich sił

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

using namespace std;

int main() {
    // Wczytaj dane wejściowe
    string operation;
    double num1, num2;
    
    cin >> operation;
    cin >> num1;
    cin >> num2;
    
    // Utwórz obiekt Calculator
    Calculator calc;
    
    // TODO: Użyj bloku try-catch, aby wykonać operację
    // - Jeśli operation to "divide", wywołaj calc.divide(num1, num2)
    // - Jeśli operation to "sqrt", wywołaj calc.safeSqrt(num1)
    // - Jeśli operacja się powiedzie, wypisz "Result: ", a następnie wynik
    // - Przechwyć wyjątki i wypisz "Error: ", a następnie komunikat wyjątku (użyj what())
    // - Po bloku try-catch zawsze wypisz "Calculation complete"
    
    
    return 0;
}
quiz iconSprawdź się

Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.

Wszystkie lekcje w sekcji Programowanie obiektowe

Poćwicz samodzielnie: Kompilator C++ online