1242.2 Langage C++ - 2024-2025
1. Gestion des erreurs
2. Principe des exceptions
3.
4. Relance d'exceptions
5.
6.
// Computes f(x) = 1/x^2
#define ERROR -1
int main()
{
int x = 0;
double result = ERROR;
cin >> x;
result = f(x);
if (ERROR == result)
cout << "erreur";
else
cout << result;
return 0;
}
double f(int x)
{
double res = ERROR;
if (false == inv(x, res)) {
cout << "inversion error";
return ERROR;
}
else
return res*res; // (1/x)^2
}
bool inv(int n, double &res)
{
if (n==0) return false;
else {
res = 1.0 / n;
return true;
}
}
Code de gestion d'erreurs mélanger au "vrai" code
L'appelant doit savoir ce que signifient les codes d'erreurs retournés
Retouner une valeur et un code d'erreur en même temps
// Computes f(x) = 1/x^2
int main()
{
int x = 0;
double result = -1;
cin >> x;
try {
result = f(x);
}
catch (...) {
cout << "erreur";
}
cout << result;
}
double f(int x)
{
double res = inv(x);
return res*res;
}
double inv(int x)
{
if (x==0) throw(0);
return 1./x;
}
Lors de la détection d'une «erreur» (fonctionnement imprévu)
Lorsqu'une exception est lancée (avec
L'exception remonte les fonctions appelantes jusqu'à être récupérée (avec
Si elle n'est pas récupérée, alors le programme se termine
Les exceptions permettent de séparer le traitement d'erreur du reste du code
Une exception n'est pas forcément une erreur (fonctionnement non prévu)
Les instructions pouvant lever une exception doivent être à l'intérieur d'un bloc :
Un bloc
Une exception est levée par l'instruction :
int main()
{
auto diviseur = 0;
try
{
if (diviseur == 0) throw diviseur;
cout << "Q:" << dividende/diviseur << endl;
}
catch (int &d) // Exception handler for int
{
cout << "Division par " << d << endl;
}
catch (...) // Exception handler for all other types
{
cout << "Autre erreur" << endl;
}
return 0;
}
Le premier gestionnaire compatible avec le type de l'exception l'attrape
Au plus un gestionnaire est exécuté pour une exception levée
Donc, si plusieurs gestionnaires sont appropriés pour le type de l'exception, seul le premier sera exécuté
Il convient donc de commencer par les gestionnaires les plus spécialisés, et terminer par le plus général
Pour être gérable, une exception doit être levée dans un bloc
try
{
throw "err!";
}
void foo()
{
throw ERROR_CODE_27;
}
try
{
foo();
}
Si l'instruction
Un bloc
try {
...
}
catch (int except1) { // Handle int exceptions
...
}
catch (std::string except2) { // Handle string exceptions
...
}
catch (std::exception &except3) { // Handle std::exception exceptions
...
}
catch (...) { // Handle all other exceptions
...
}
try {
switch(i) {
case 1: throw 2; break;
case 2: throw "Erreur"; break;
case 3: throw objet; break;
}
}
catch(int x) {
cout << x;
}
catch(const char* str) {
cout << str;
}
catch(Classe &obj) {
cout << obj->afficher();
}
Lorsqu'une exception de type T est levée, le compilateur cherche un gestionnaire :
1. de type
2. de superclasse de
3. pointeur sur une classe dérivée
4. type indéterminé :
Si l'exception est levée dans une fonction, le compilateur cherche un gestionnaire dans la fonction. Si ce n'est pas le cas, dans la fonction appelante, etc.
Le gestionnaire peut mettre fin au programme. Si ce n'est pas le cas, l'exécution continue par l'instruction qui suit les gestionnaires :
Si aucun gestionnaire n'est trouvé, il appelle la fonction
Le principal problème lié aux exceptions est la libération des ressources :
- Libérer les ressources allouées dynamiquement
- Fermer les flux
1. Les allocateurs
2. RAII (Resource Acquisition Is Initialization)
Il est possible de relancer une exception déjà lancée
Il faut utiliser l'instruction
void foo()
{
try {
int n=2;
throw n;
}
catch(int) {
cout<<"foo \n";
throw;
}
}
int main()
{
try {
foo();
}
catch(int) {
cout <<"main\n";
exit(-1);
}
return 0;
}
Le mot clé
Il est possible de spécifier une condition pour lever une exception
// Do not throw an exception
void f1() noexcept(true) { ... }
// Might throw an exception
void f2() noexcept(false) { ... }
// Might throw an exception if expr is false
void f3() noexcept(expr) { ... }
// Might throw an exception if foo() may throw too
void f4() noexcept(noexcept(foo())) { ... }
La bibliothèque standard définit des classes exceptions
Leur superclasse commune est
Elle est définie dans le fichier d'en-tête
Elle comporte une méthode
Les exceptions personnalisées hériteront donc de
Double intérêt :
1. Transmettre des informations (attributs, what())
2. catch (const exception &e) plutôt que catch(...)
Les exceptions sont lancées par valeur et attrapées par référence
#include <iostream>
#include <stdexcept> // or #include <exception>
using namespace std;
class Exception1 : public exception {
public:
Exception1() noexcept {}
const char* what() const noexcept override {
return "exception1";
}
};
class Exception2 : public exception {
public:
Exception2() noexcept {}
const char* what() const noexcept override {
return "exception2";
}
};
int main()
{
try {
cout << "bloc try 1" << endl;
throw Exception1();
}
catch(const Exception1 & e) {
cout << e.what() << endl;
}
try {
cout << "bloc try 2" << endl;
throw Exception2();
}
catch (const exception & e) {
cout << e.what() << endl;
}
return 0;
}