Gestion des Exceptions en C++

Bonjour à tous, aspirants programmeurs ! Aujourd'hui, nous allons entreprendre un voyage passionnant dans le monde de la Gestion des Exceptions en C++. En tant que professeur de sciences informatiques de votre quartier, je suis là pour vous guider à travers ce sujet important. Alors, prenez votre boisson préférée, mettez-vous à l'aise, et plongeons-y !

C++ Exception Handling

Qu'est-ce qu'une Exception ?

Avant de lancer et de capturer des exceptions comme des jongleurs chevronnés, comprenons ce qu'elles sont. Dans le monde de la programmation, les exceptions sont des événements inattendus qui se produisent lors de l'exécution d'un programme. Elles sont comme ces quiz imprévus que j'ai souvent donnés (désolé pour ça !) – imprévus et parfois un peu difficiles à gérer.

Les exceptions perturbent le flux normal des instructions d'un programme. Elles peuvent être causées par divers facteurs, tels que :

  1. Division par zéro
  2. Accès à une plage hors limites d'un tableau
  3. Manque de mémoire
  4. Tentative d'ouverture d'un fichier qui n'existe pas

Maintenant, voyons comment C++ nous permet de gérer ces situations inattendues avec grâce.

Lancer des Exceptions

Les Bases du Lancement

En C++, nous utilisons le mot-clé throw pour lever une exception. C'est comme lever la main en classe quand vous avez une question ou un problème. Voici un exemple simple :

#include <iostream>
using namespace std;

int main() {
try {
throw 20;
}
catch (int e) {
cout << "Une exception s'est produite. Exception Nr. " << e << endl;
}
return 0;
}

Dans cet exemple, nous lançons une exception entière avec la valeur 20. Mais ne vous inquiétez pas, nous allons la capturer dans un instant !

Lancer des Types Différents

C++ est flexible et permet de lancer des exceptions de divers types. Regardons un exemple plus pratique :

#include <iostream>
#include <stdexcept>
using namespace std;

double diviser(int a, int b) {
if (b == 0) {
throw runtime_error("Division par zéro !");
}
return static_cast<double>(a) / b;
}

int main() {
try {
cout << diviser(10, 2) << endl;  // Ça va bien marcher
cout << diviser(10, 0) << endl;  // Ça va lever une exception
}
catch (const runtime_error& e) {
cout << "Exception capturée : " << e.what() << endl;
}
return 0;
}

Dans cet exemple, nous levons une exception runtime_error lorsque quelqu'un tente de diviser par zéro. C'est comme mettre un panneau "Pas de Division par Zéro" dans notre quartier mathématique !

Capturer des Exceptions

Les Bases de la Capture

Maintenant que nous savons comment lancer des exceptions, apprenons à les capturer. Capturer des exceptions est comme être un propriétaire de pet responsable – vous devez être prêt à gérer ce que votre code vous jette !

Nous utilisons un bloc try-catch pour capturer des exceptions. Le bloc try contient le code qui pourrait lever une exception, et le bloc catch gère l'exception si elle se produit.

#include <iostream>
using namespace std;

int main() {
try {
int age = -5;
if (age < 0) {
throw "L'âge ne peut pas être négatif !";
}
cout << "L'âge est : " << age << endl;
}
catch (const char* msg) {
cerr << "Erreur : " << msg << endl;
}
return 0;
}

Dans cet exemple, nous vérifions si l'âge est négatif. Si c'est le cas, nous levons une exception avec un message d'erreur personnalisé.

Capturer plusieurs Exceptions

Parfois, différents types d'exceptions peuvent être levées à partir du même morceau de code. Dans de tels cas, nous pouvons avoir plusieurs blocs catch :

#include <iostream>
#include <stdexcept>
using namespace std;

int main() {
try {
int choix;
cout << "Entrez 1 pour une exception entière, 2 pour une erreur d'exécution : ";
cin >> choix;

if (choix == 1) {
throw 42;
} else if (choix == 2) {
throw runtime_error("Une erreur d'exécution sauvage est apparue !");
} else {
throw "Choix inconnu !";
}
}
catch (int e) {
cout << "Exception entière capturée : " << e << endl;
}
catch (const runtime_error& e) {
cout << "Erreur d'exécution capturée : " << e.what() << endl;
}
catch (...) {
cout << "Exception inconnue capturée !" << endl;
}
return 0;
}

Cet exemple montre comment nous pouvons capturer différents types d'exceptions. Le bloc catch (...) est un filet de sécurité qui gérera toute exception non capturée par les blocs catch précédents. C'est comme avoir un filet de sécurité pour toutes ces surprises imprévues !

Exceptions Standards en C++

C++ vient avec un ensemble d'exceptions standard que vous pouvez utiliser dans vos programmes. Ce sont comme les couteaux suisses du monde des exceptions – polyvalents et toujours prêts à aider !

Voici un tableau de quelques exceptions standard couramment utilisées :

Exception Description
std::runtime_error Erreurs logiques d'exécution
std::logic_error Erreurs logiques
std::out_of_range Accès au-delà de la plage
std::overflow_error Débordement arithmétique
std::bad_alloc Échec d'allocation de mémoire

Regardons un exemple utilisant une exception standard :

#include <iostream>
#include <vector>
#include <stdexcept>
using namespace std;

int main() {
vector<int> numbers = {1, 2, 3, 4, 5};
try {
cout << numbers.at(10) << endl;  // Ça va lever une exception out_of_range
}
catch (const out_of_range& e) {
cerr << "Erreur de plage : " << e.what() << endl;
}
return 0;
}

Dans cet exemple, nous essayons d'accéder à un élément hors plage de notre vecteur. La fonction at() lève une exception out_of_range lorsque cela se produit.

Définir de Nouvelles Exceptions

Bien que les exceptions standard soient excellentes, parfois vous avez besoin de quelque chose de plus adapté à vos besoins spécifiques. C'est là que les exceptions personnalisées entrent en jeu !

Voici comment vous pouvez définir votre propre classe d'exception :

#include <iostream>
#include <exception>
using namespace std;

class NegativeValueException : public exception {
public:
const char* what() const throw() {
return "Les valeurs négatives ne sont pas autorisées !";
}
};

double squareRoot(double x) {
if (x < 0) {
throw NegativeValueException();
}
return sqrt(x);
}

int main() {
try {
cout << squareRoot(25) << endl;  // Ça va bien marcher
cout << squareRoot(-5) << endl;  // Ça va lever notre exception personnalisée
}
catch (const NegativeValueException& e) {
cerr << "Erreur : " << e.what() << endl;
}
return 0;
}

Dans cet exemple, nous avons créé une classe NegativeValueException personnalisée. Nous l'utilisons dans notre fonction squareRoot pour lever une exception lorsque quelqu'un tente de calculer la racine carrée d'un nombre négatif.

Et voilà, les amis ! Nous avons couvert les bases de la Gestion des Exceptions en C++. Souvenez-vous, les exceptions sont vos amies. Elles vous aident à écrire un code plus robuste et résistant aux erreurs. Continuez à pratiquer, et bientôt vous serez en mesure de gérer des exceptions comme un professionnel !

Bon codage, et que vos exceptions soient toujours capturées !

Credits: Image by storyset