Introduction : Pourquoi la gestion des erreurs est le fondement d’un code fiable en C++
Tout programme interagissant avec le monde extérieur (fichiers, réseau, saisie utilisateur) rencontre inévitablement des situations exceptionnelles. En C++, le mécanisme try/catch est l’outil principal pour gérer ces situations de manière élégante et contrôlée. Dans cet article, nous analyserons la gestion des erreurs en C++, des constructions de base aux techniques avancées, y compris les bonnes pratiques et les pièges à éviter.
1. Les bases : try, catch et throw
Le mécanisme des exceptions en C++ repose sur trois mots-clés : try, catch et throw.
1.1. Blocs try et catch
Le bloc try contient le code qui peut lever une exception. Le bloc catch intercepte cette exception et la traite.
#include <iostream>#include <stdexcept>
int main() { try { // Code pouvant provoquer une erreur int age = -5; if (age < 0) { throw std::invalid_argument("L'âge ne peut pas être négatif"); } std::cout << "Âge : " << age << std::endl; } catch (const std::invalid_argument& e) { std::cerr << "Erreur : " << e.what() << std::endl; } return 0;}1.2. Types d’exceptions
En C++, vous pouvez lever des exceptions de n’importe quel type : nombres, chaînes, objets de classes. Cependant, il est recommandé d’utiliser les classes d’exceptions standard de <stdexcept> ou de créer vos propres classes héritant de std::exception.
- Exceptions standard :
std::runtime_error,std::logic_error,std::out_of_range, etc. - Exceptions personnalisées : créées par héritage de
std::exception.
class MyException : public std::exception {public: const char* what() const noexcept override { return "Mon exception personnalisée"; }};1.3. Blocs catch multiples
Vous pouvez intercepter différents types d’exceptions dans des blocs catch séparés. L’ordre est important : les exceptions les plus spécifiques doivent venir en premier.
try { // code} catch (const std::invalid_argument& e) { // traitement de invalid_argument} catch (const std::exception& e) { // traitement de toutes les autres exceptions de std} catch (...) { // catch-all : intercepte tout ce qui n’a pas été attrapé ci-dessus}2. Techniques avancées : déroulement de la pile et RAII
2.1. Déroulement de la pile (Stack Unwinding)
Lorsqu’une exception est levée, C++ détruit automatiquement tous les objets locaux sur le chemin du point throw jusqu’au catch correspondant. Ce processus est appelé déroulement de la pile. Il garantit l’appel des destructeurs pour tous les objets créés dans les blocs try.
class Resource {public: Resource() { std::cout << "Ressource créée"; } ~Resource() { std::cout << "Ressource détruite"; }};
void riskyFunction() { Resource res; throw std::runtime_error("Quelque chose a mal tourné");}
int main() { try { riskyFunction(); } catch (const std::exception& e) { std::cerr << e.what() << std::endl; } // Sortie : Ressource créée -> Ressource détruite -> Quelque chose a mal tourné}2.2. RAII (Resource Acquisition Is Initialization)
RAII est un idiome clé du C++ étroitement lié à la gestion des erreurs. L’idée : la gestion des ressources (mémoire, fichiers, mutex) est liée à la durée de vie de l’objet. Le destructeur libère automatiquement la ressource, même en cas d’exception.
- Pointeurs intelligents :
std::unique_ptr,std::shared_ptr