Menu
Coddy logo textTech

Własne hierarchie wyjątków

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

Chociaż biblioteka standardowa udostępnia przydatne klasy wyjątków, takie jak std::runtime_error, aplikacje używane w rzeczywistych warunkach często zyskują na tworzeniu własnych hierarchii wyjątków. Definiując własne klasy wyjątków dziedziczące po wyjątkach standardowych, możesz tworzyć typy błędów specyficzne dla danej domeny, które zawierają dodatkowy kontekst i umożliwiają precyzyjniejszą obsługę błędów.

Klasa własnych wyjątków zazwyczaj dziedziczy po std::exception lub jednej z jej klas pochodnych i nadpisuje metodę what():

#include <exception>
#include <string>

class DatabaseException : public std::exception {
    std::string message;
public:
    DatabaseException(const std::string& msg) : message(msg) {}
    const char* what() const noexcept override { return message.c_str(); }
};

class ConnectionException : public DatabaseException {
public:
    ConnectionException(const std::string& host) 
        : DatabaseException("Failed to connect to: " + host) {}
};

class QueryException : public DatabaseException {
public:
    QueryException(const std::string& query) 
        : DatabaseException("Invalid query: " + query) {}
};

Ta hierarchia pozwala blokom catch obsługiwać wyjątki na różnych poziomach szczegółowości:

try {
    // operacje na bazie danych...
    throw ConnectionException("localhost");
} catch (const ConnectionException& e) {
    std::cout << "Connection issue: " << e.what() << "\n";
} catch (const DatabaseException& e) {
    std::cout << "Database error: " << e.what() << "\n";
} catch (const std::exception& e) {
    std::cout << "General error: " << e.what() << "\n";
}

Specyfikator noexcept przy what() jest ważny — gwarantuje, że metoda nie zgłosi wyjątku, czego wymaga interfejs klasy bazowej. Uporządkuj bloki catch od najbardziej szczegółowego do najbardziej ogólnego, ponieważ C++ używa pierwszego pasującego programu obsługi.

challenge icon

Wyzwanie

Łatwy

Zbudujmy system przetwarzania plików z własną hierarchią wyjątków, która obsługuje różne rodzaje błędów związanych z plikami. Utworzysz wyjątki specyficzne dla domeny, dziedziczące po bazowej klasie wyjątków, co umożliwi precyzyjną obsługę błędów na różnych poziomach szczegółowości.

Podzielisz kod na trzy pliki:

  • FileExceptions.h: Zdefiniuj własną hierarchię wyjątków dla operacji na plikach.

    Utwórz bazową klasę FileException, która dziedziczy po std::exception. Powinna przechowywać komunikat o błędzie i nadpisywać metodę what() ze specyfikatorem noexcept.

    Następnie utwórz dwie klasy wyjątków pochodnych:

    • FileNotFoundException: przyjmuje nazwę pliku i tworzy komunikat: File not found: [filename]
    • PermissionDeniedException: przyjmuje nazwę pliku i tworzy komunikat: Permission denied: [filename]

    Każda klasa pochodna powinna wywołać konstruktor klasy nadrzędnej z odpowiednim komunikatem.

  • FileProcessor.h: Zdefiniuj klasę FileProcessor, która symuluje operacje na plikach i zgłasza własne wyjątki.

    Klasa FileProcessor powinna mieć metodę openFile(const std::string& filename), która symuluje otwieranie pliku:

    • Jeśli nazwa pliku zawiera missing, zgłoś wyjątek FileNotFoundException
    • Jeśli nazwa pliku zawiera locked, zgłoś wyjątek PermissionDeniedException
    • W przeciwnym razie zwróć ciąg znaków Successfully opened: [filename]
  • main.cpp: Odczytaj jedną nazwę pliku z wejścia.

    Utwórz obiekt FileProcessor i spróbuj otworzyć plik. Użyj bloku try-catch z procedurami obsługi ułożonymi od najbardziej szczegółowej do najbardziej ogólnej:

    1. Przechwyć FileNotFoundException i wypisz: File Error: [message]
    2. Przechwyć PermissionDeniedException i wypisz: Access Error: [message]
    3. Przechwyć FileException i wypisz: General File Error: [message]

    Jeśli nie zostanie zgłoszony żaden wyjątek, wypisz komunikat o powodzeniu zwrócony przez openFile().

Na przykład dla wejścia report.txt:

Successfully opened: report.txt

Dla wejścia missing_data.csv:

File Error: File not found: missing_data.csv

Dla wejścia locked_config.ini:

Access Error: Permission denied: locked_config.ini

To wyzwanie pokazuje, jak własne hierarchie wyjątków pozwalają obsługiwać błędy na odpowiednim poziomie szczegółowości: przechwytując konkretne wyjątki, gdy potrzebujesz precyzyjnej obsługi, lub przechwytując wyjątek bazowy, gdy chcesz jednolicie obsłużyć wszystkie błędy dotyczące plików.

Spróbuj swoich sił

#include <iostream>
#include <string>
#include "FileExceptions.h"
#include "FileProcessor.h"

using namespace std;

int main() {
    string filename;
    cin >> filename;
    
    FileProcessor processor;
    
    // TODO: Użyj bloku try-catch do obsługi wyjątków
    // Uporządkuj bloki obsługi od najbardziej szczegółowego do najbardziej ogólnego:
    // 1. Przechwyć FileNotFoundException — wypisz: "File Error: [message]"
    // 2. Przechwyć PermissionDeniedException — wypisz: "Access Error: [message]"
    // 3. Przechwyć FileException — wypisz: "General File Error: [message]"
    // Jeśli nie wystąpi wyjątek, wypisz komunikat o powodzeniu zwrócony przez openFile()
    
    try {
        // TODO: Wywołaj processor.openFile() i obsłuż wynik
        
    }
    // TODO: Dodaj bloki catch w kolejności od najbardziej szczegółowego do najbardziej ogólnego
    
    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