Hiérarchies d'exceptions personnalisées
Fait partie de la section Programmation Orientée Objet du Journey C++ de Coddy — leçon 82 sur 104.
Alors que la bibliothèque standard fournit des classes d'exception utiles comme std::runtime_error, les applications concrètes bénéficient souvent de la création de hiérarchies d'exceptions personnalisées. En définissant vos propres classes d'exception qui héritent des exceptions standard, vous pouvez créer des types d'erreurs spécifiques au domaine qui transportent un contexte supplémentaire et permettent une gestion des erreurs plus précise.
Une classe d'exception personnalisée hérite généralement de std::exception ou de l'une de ses classes dérivées et surcharge la méthode 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) {}
};Cette hiérarchie permet aux blocs catch de gérer les exceptions à différents niveaux de spécificité :
try {
// opérations de base de données...
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";
}Le spécificateur noexcept sur what() est important - il garantit que la méthode ne lèvera pas d'exception, ce qui est requis par l'interface de la classe de base. Ordonnez vos blocs catch du plus spécifique au plus général, car le C++ utilise le premier gestionnaire correspondant.
Défi
FacileConstruisons un système de traitement de fichiers avec une hiérarchie d'exceptions personnalisées qui gère différents types d'erreurs liées aux fichiers. Vous allez créer des exceptions spécifiques au domaine qui héritent d'une classe d'exception de base, permettant une gestion précise des erreurs à différents niveaux de spécificité.
Vous organiserez votre code sur trois fichiers :
FileExceptions.h: Définissez votre hiérarchie d'exceptions personnalisées pour les opérations sur les fichiers.Créez une classe de base
FileExceptionqui hérite destd::exception. Elle doit stocker un message d'erreur et surcharger la méthodewhat()avec le spécificateurnoexcept.Ensuite, créez deux classes d'exception dérivées :
FileNotFoundException— prend un nom de fichier et crée un message :File not found: [filename]PermissionDeniedException— prend un nom de fichier et crée un message :Permission denied: [filename]
Chaque classe dérivée doit appeler son constructeur parent avec le message approprié.
FileProcessor.h: Définissez une classeFileProcessorqui simule des opérations sur les fichiers et lève vos exceptions personnalisées.Votre
FileProcessordoit avoir une méthodeopenFile(const std::string& filename)qui simule l'ouverture d'un fichier :- Si le nom du fichier contient
missing, levez uneFileNotFoundException - Si le nom du fichier contient
locked, levez unePermissionDeniedException - Sinon, retournez la chaîne
Successfully opened: [filename]
- Si le nom du fichier contient
main.cpp: Lisez un seul nom de fichier à partir de l'entrée.Créez un
FileProcessoret tentez d'ouvrir le fichier. Utilisez un bloc try-catch avec des gestionnaires ordonnés du plus spécifique au plus général :- Interceptez
FileNotFoundExceptionet affichez :File Error: [message] - Interceptez
PermissionDeniedExceptionet affichez :Access Error: [message] - Interceptez
FileExceptionet affichez :General File Error: [message]
Si aucune exception n'est levée, affichez le message de succès retourné par
openFile().- Interceptez
Par exemple, avec l'entrée report.txt :
Successfully opened: report.txtAvec l'entrée missing_data.csv :
File Error: File not found: missing_data.csvAvec l'entrée locked_config.ini :
Access Error: Permission denied: locked_config.iniCe défi démontre comment les hiérarchies d'exceptions personnalisées vous permettent de gérer les erreurs au niveau de détail approprié — en interceptant des exceptions spécifiques lorsque vous avez besoin d'une gestion précise, ou en interceptant l'exception de base lorsque vous souhaitez gérer toutes les erreurs de fichier de manière uniforme.
Aide-mémoire
Les classes d'exception personnalisées héritent de std::exception ou de ses classes dérivées et surchargent la méthode 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(); }
};Créez des hiérarchies d'exceptions en héritant d'exceptions de base personnalisées :
class ConnectionException : public DatabaseException {
public:
ConnectionException(const std::string& host)
: DatabaseException("Failed to connect to: " + host) {}
};Gérez les exceptions à différents niveaux de spécificité en ordonnant les blocs catch du plus spécifique au plus général :
try {
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";
}Le spécificateur noexcept sur what() garantit que la méthode ne lèvera pas d'exception, ce qui est requis par l'interface de la classe de base.
Essayez vous-même
#include <iostream>
#include <string>
#include "FileExceptions.h"
#include "FileProcessor.h"
using namespace std;
int main() {
string filename;
cin >> filename;
FileProcessor processor;
// TODO: Utiliser un bloc try-catch pour gérer les exceptions
// Ordonner les gestionnaires du plus spécifique au plus général :
// 1. Intercepter FileNotFoundException - afficher : "File Error: [message]"
// 2. Intercepter PermissionDeniedException - afficher : "Access Error: [message]"
// 3. Intercepter FileException - afficher : "General File Error: [message]"
// Si aucune exception n'est levée, afficher le message de succès de openFile()
try {
// TODO: Appeler processor.openFile() et gérer le résultat
}
// TODO: Ajouter des blocs catch dans l'ordre du plus spécifique au plus général
return 0;
}
Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.
Toutes les leçons de Programmation Orientée Objet
1Fondamentaux de la POO
Fichiers externesBuild et Compilation C++Fichiers d'en-tête et Fichiers sourceEspaces de noms et PortéeIntroduction à la POO en C++Classes vs ObjetsLe pointeur 'this'Méthodes (Fonctions membres)Attributs (Membres de données)Bases des Ctors et DtorsRécapitulatif - Calculatrice simple4Propriétés de classe
Membres d'instance vs membres statiquesGetters et SettersFonctions membres constMot-clé mutableMéthodes et variables statiquesFonctions et classes amiesRécapitulatif - Gestionnaire de compte bancaire7Héritage
Héritage de baseNiveaux d'accès à l'héritageOrdre d'appel des Ctor et DtorRedéfinition de méthodesFonctions virtuelles et VTableHéritage multipleHéritage virtuelRécapitulatif - Hiérarchie des employés10Présentation de la STL
Présentation et philosophie de la STLConteneurs de la STLItérateursAlgorithmes de la STLFoncteurs et expressions lambdaRécapitulatif - Fréquence des mots13Patrons de conception, Partie 1
Introduction aux patrons de conceptionPatron SingletonFabrique & Fabrique abstraitePatron MonteurPatron ObservateurPatron Stratégie2Gestion de la mémoire
Mémoire Pile vs TasPointeurs et RéférencesMémoire dynamique (new/delete)Smart Pointers en C++RAII en C++Récapitulatif - Gestionnaire de tableaux dynamiques5Encapsulation
Spécificateurs d'accès en C++Spécificateurs d'accès en profondeurMasquage d'informationStruct vs ClassClasses imbriquées et internesRécapitulatif - Système de gestion d'étudiants8Polymorphisme
Polymorphisme : Compilation vs ExécutionSurcharge de fonctionsRetour sur les fonctions virtuellesFonctions virtuelles puresClasses abstraitesConception d'interfaces en C++Dynamic Casting & RTTIRécapitulatif - Calculateur de formes11Concepts avancés de la POO
Composition vs HéritageMixins via CRTPIdiome PimplEffacement de typeEnum Classes & Typage fortGestion des exceptions en POOHiérarchies d'exceptions personnalisées14Patrons de conception, Partie 2
Patron CommandePatron AdaptateurPatron DécorateurPatron Template MethodPatron ÉtatPatron CompositeRAII en tant que patron3Constructeurs et Destructeurs
Constructeur par défautConstructeur paramétréConstructeur de copieConstructeur de déplacementListes d'initialisation du constructeurConstructeurs déléguésAnalyse approfondie du destructeurRègle de trois / cinq / zéroRécapitulatif - Classe String6Surcharge d'opérateurs
Introduction à la surcharge d'opérateursSurcharge des opérateurs arithmétiquesSurcharge des opérateurs de comparaisonOpérateurs de fluxSurcharge de l'opérateur d'affectationSurcharge des opérateurs [] et ()Opérateurs de conversion de typeRécapitulatif - Classe Matrix9Templates
Templates de fonctionsTemplates de classesSpécialisation de templatesTemplates variadiquesBases de SFINAE & Type TraitsRécapitulatif - Conteneur générique