Gerarchie personalizzate di eccezioni
Fa parte della sezione Programmazione orientata agli oggetti del percorso C++ di Coddy. Lezione 82 di 104.
Sebbene la libreria standard fornisca classi di eccezione utili come std::runtime_error, le applicazioni del mondo reale spesso traggono vantaggio dalla creazione di gerarchie di eccezioni personalizzate. Definendo le tue classi di eccezione che ereditano dalle eccezioni standard, puoi creare tipi di errore specifici del dominio che includono un contesto aggiuntivo e consentono una gestione degli errori più precisa.
Una classe di eccezione personalizzata eredita in genere da std::exception o da una delle sue classi derivate e sovrascrive il metodo 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) {}
};Questa gerarchia consente ai blocchi catch di gestire le eccezioni con diversi livelli di specificità:
try {
// operazioni sul database...
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";
}Lo specificatore noexcept su what() è importante: garantisce che il metodo non generi eccezioni, requisito dell’interfaccia della classe base. Ordina i blocchi catch dal più specifico al più generale, poiché C++ usa il primo gestore corrispondente.
Sfida
FacileCostruiamo un sistema di elaborazione dei file con una gerarchia personalizzata di eccezioni che gestisce diversi tipi di errori relativi ai file. Creerai eccezioni specifiche del dominio che ereditano da una classe di eccezione di base, consentendo una gestione precisa degli errori a diversi livelli di specificità.
Organizzerai il codice in tre file:
FileExceptions.h: definisci la gerarchia personalizzata di eccezioni per le operazioni sui file.Crea una classe di base
FileExceptionche eredita dastd::exception. Dovrebbe memorizzare un messaggio di errore e sovrascrivere il metodowhat()con lo specificatorenoexcept.Poi crea due classi di eccezione derivate:
FileNotFoundException: accetta un nome file e crea un messaggio:File not found: [filename]PermissionDeniedException: accetta un nome file e crea un messaggio:Permission denied: [filename]
Ogni classe derivata dovrebbe chiamare il costruttore della classe genitore con il messaggio appropriato.
FileProcessor.h: definisci una classeFileProcessorche simula le operazioni sui file e genera le tue eccezioni personalizzate.La classe
FileProcessordovrebbe avere un metodoopenFile(const std::string& filename)che simula l'apertura di un file:- Se il nome del file contiene
missing, genera unaFileNotFoundException - Se il nome del file contiene
locked, genera unaPermissionDeniedException - Altrimenti, restituisce la stringa
Successfully opened: [filename]
- Se il nome del file contiene
main.cpp: leggi un singolo nome file dall'input.Crea un
FileProcessore prova ad aprire il file. Usa un blocco try-catch con gestori ordinati dal più specifico al più generale:- Intercetta
FileNotFoundExceptione stampa:File Error: [message] - Intercetta
PermissionDeniedExceptione stampa:Access Error: [message] - Intercetta
FileExceptione stampa:General File Error: [message]
Se non viene generata alcuna eccezione, stampa il messaggio di successo restituito da
openFile().- Intercetta
Per esempio, con l'input report.txt:
Successfully opened: report.txtCon l'input missing_data.csv:
File Error: File not found: missing_data.csvCon l'input locked_config.ini:
Access Error: Permission denied: locked_config.iniQuesta sfida dimostra come le gerarchie personalizzate di eccezioni consentano di gestire gli errori con il livello di dettaglio appropriato: intercettando eccezioni specifiche quando serve una gestione precisa, oppure intercettando l'eccezione di base quando si desidera gestire tutti gli errori dei file in modo uniforme.
Provalo tu
#include <iostream>
#include <string>
#include "FileExceptions.h"
#include "FileProcessor.h"
using namespace std;
int main() {
string filename;
cin >> filename;
FileProcessor processor;
// TODO: Use a try-catch block to handle exceptions
// Order handlers from most specific to most general:
// 1. Catch FileNotFoundException - print: "File Error: [message]"
// 2. Catch PermissionDeniedException - print: "Access Error: [message]"
// 3. Catch FileException - print: "General File Error: [message]"
// If no exception, print the success message from openFile()
try {
// TODO: Call processor.openFile() and handle the result
}
// TODO: Add catch blocks in order from most specific to most general
return 0;
}
Questa lezione include un breve quiz. Inizia la lezione per rispondere e tenere traccia dei tuoi progressi.
Tutte le lezioni di Programmazione orientata agli oggetti
1Fondamenti della programmazione orientata agli oggetti
File esterniBuild e compilazione in C++File header e file sorgenteNamespace e ambitoIntroduzione alla programmazione orientata agli oggetti in C++Classi e oggetti a confrontoIl puntatore 'this'Metodi (funzioni membro)Attributi (membri dati)Fondamenti di costruttori e distruttoriRiepilogo - Calcolatrice semplice4Proprietà delle classi
Membri di istanza e staticiGetter e setterFunzioni membro constParola chiave mutableMetodi e variabili staticiFunzioni e classi friendRiepilogo - Gestore di conti bancari7Ereditarietà
Ereditarietà di baseLivelli di accesso nell’ereditarietàOrdine di chiamata di costruttori e distruttoriRidefinizione dei metodiFunzioni virtuali e VTableEreditarietà multiplaEreditarietà virtualeRiepilogo - Gerarchia dei dipendenti2Gestione della memoria
Memoria Stack vs HeapPuntatori e riferimentiMemoria dinamica (new/delete)Puntatori intelligenti in C++RAII in C++Riepilogo - Gestore di array dinamico5Incapsulamento
Specificatori di accesso in C++Specificatori di accesso in dettaglioOccultamento delle informazioniStruct vs classClassi annidate e interneRiepilogo - Sistema di registrazione degli studenti8Polimorfismo
Polimorfismo a compile time e a runtimeOverload delle funzioniFunzioni virtuali: ripassoFunzioni virtuali pureClassi astratteProgettazione delle interfacce in C++Dynamic casting e RTTIRipasso: calcolatrice di forme11Concetti avanzati di OOP
Composizione vs ereditarietàMixin tramite CRTPIdiom PimplType ErasureEnum class e tipizzazione forteGestione delle eccezioni in OOPGerarchie personalizzate di eccezioni14Pattern di progettazione - Parte 2
Pattern CommandPattern AdapterPattern DecoratorPattern Template MethodPattern StatePattern CompositeRAII come pattern3Costruttori e distruttori
Costruttore predefinitoCostruttore con parametriCostruttore di copiaCostruttore di spostamentoListe di inizializzazione dei costruttoriCostruttori delegantiApprofondimento sui distruttoriRegola del Tre / Cinque / ZeroRipasso - classe String6Sovraccarico degli operatori
Introduzione al sovraccarico degli operatoriSovraccarico degli operatori aritmeticiSovraccarico degli operatori di confrontoOperatori di flussoSovraccarico dell'operatore di assegnazioneSovraccarico degli operatori [] e ()Operatori di conversione di tipoRipasso - Classe Matrix9Template
Template di funzioneTemplate di classeSpecializzazione dei templateTemplate variadiciBasi di SFINAE e dei trait di tipoRiepilogo - Contenitore genericoEsercitati da solo: Compilatore C++ online