====== Partie IV – S’assurer qu’un code soit toujours effectué, qu’une exception se produise ou ne se produise pas ======
**(A faire en dehors du TD)**
[[in204:tds:sujets:td7|TD7]]
En C# (et aussi en java), vous avez la possibilité d’écrire un bloc finally.
try
{
…
}
finally
{
// Ce code est toujours exécuté
// Qu’une exception se produise
// Que cette exception soit capturée
// Que cette exception ne soit pas capturée
// Qu’une exception soit générée dans le traitement
// de l’exception.
// Qu’aucune exception ne se produise.
}
Le code qui se trouve dans le bloc du finally est toujours exécuté, qu’une exception se produise ou qu’aucune exception ne se produise.
===== Question n°1 =====
En C++, cette construction n’existe pas. Est-ce qu’il est possible de garantir la même chose en C++ en utilisant des blocs catch ?
try
{
…
}
catch(type1 e)
{
// Capture les exceptions dérivant de type1
}
catch(type1 e)
{
// Capture les exceptions dérivant de type2 si elles n’ont pas été capturées
// par les exceptions de type1.
}
catch(…)
{
// Capture toutes les exceptions n’ayant pas été capturées par une clause
// précédente
}
Nous avons deux cas à traiter, soit une exception est propagée, soit aucune exception n'est propagée. Dans ce cas, nous devons avoir pour la fonction la structure suivante :
return_type function(...)
{
return_type value;
try
{
// Code du corps de la fonction.
// Il n'est pas possibile d'utiliser l'instruction ''return''.
}
catch(...)
{
// Code à exécuter à la fin de la fonction
throw;
// Réamorce l'exception capturée.
}
// Code à exécuter à la fin de la fonction
return value;
}
===== Question n°2 =====
Nous souhaitons que la fonction ''divide'' définie à la question précédente affiche le message « fonction terminée » à la fin de la fonction et même si une exception a été levée.
Comment faire avec la solution proposée à la question précédente ?
Quel est le défaut de cette approche ?
Pour la fonction ''divide'' qui peut générer une fonction, nous allons devoir gérer les deux cas de figure, le premier cas où la fonction génère une exception, le second cas où la fonction ne génère pas d'exception. Ce qui nous donne le cas de figure suivant :
double divide(double theNumerator, double theDivisor)
throw(division_by_zero)
{
double result;
try
{
if (theDivisor == 0)
throw division_by_zero();
result = theNumerator / theDivisor;
}
catch(...)
{
std::cout << "Fonction terminée" << std::endl;
throw;
}
std::cout << "Fonction terminée" << std::endl;
return result;
}
Cette approche n'est pas satisfaisante pour les raisons suivantes :
* la duplication de code, nous devons dupliquer au moins deux fois le code indiquant s'exécutant à la fin de la fonction (ceci peut-être compensé par un appel à une fonction qui factorise le code devant être exécuté en fin de la fonction).
* la nécessité de devoir stocker le résultat pour pouvoir ensuite le retourner après avoir exécuter le code de fin
* l'impossibilité d'avoir des points de sortie (''return'') multiples. Ceci peut-être compensé par des ''goto'' à une section finale correspondant à la fin de la fonction,
===== Question n°3 =====
Nous suggérons de contourner la difficulté en utilisant un objet. Nous savons qu’un objet alloué sur la pile est systématiquement détruit lorsqu’il sort du bloc d’activation.
Lorsqu’un objet est détruit, son destructeur est appelé.
Remarque : Dans le destructeur, il est possible de faire exécuter le code qui aurait été mis dans le bloc finally de C# ou de JAVA et que nous souhaitons systématiquement voir exécuté.
Compléter la classe suivante pour qu’elle affiche le message « fonction terminée » lorsque l’objet est détruit.
class finally
{
public :
finally(){}
~finally() {…}
…
}
===== Question n°3 =====
Mettez en œuvre cette solution en lieu et place de la solution proposée à la question 1.2.
Vérifiez que cette solution fonctionne correctement.
Pour la fonction ''divide'', nous pouvons définir la classe:
class divide_finally
{
public :
divide_finally(){}
~divide_finally()
{
std::cout << "Fonction terminée" << std::endl;
}
};
et nous pouvons écrire le code de la fonction ''divide'' comme suit :
double divide(double theNumerator, double theDivisor)
throw(division_by_zero)
{
divide_finally finally;
if (theDivisor == 0)
throw division_by_zero();
return theNumerator / theDivisor;
}
En fait, quand la fonction est appelée, la premier objet créé est un objet vide de type ''divide_finally''. Lorsque nous quittons la fonction, soit suite à une exception, soit suite à un ''return'', soit au fait que nous sommes arrivés à la fin de la fonction, les objets créés sont détruits dans l'ordre inverse de création. en conséquence, l'objet ''divide_finally'' sera le dernier objet créé, il sera détruit après tous les autres objets. Quand cet objet sera détruit, il appelera son destructeur qui contient le code à exécuter au moment de la fin de la fonction. Donc nous garantissons ainsi que ce code sera exécuté à la fin de la fonction.
Au passage, ceci fonctionne aussi pour des portées. Ainsi nous pouvons définir un deuxième objet de type finally que nous allons appeller ''divide_check_finally'' qui exécute le code une fois que le diviseur a été comparé à zéro.
class divide_check_finally
{
public :
divide_check_finally(){}
~divide_check_finally()
{
std::cout << "Test diviseur == 0 terminé" << std::endl;
}
};
et nous pouvons désormais écrire:
double divide(double theNumerator, double theDivisor)
throw(division_by_zero)
{
divide_finally finally;
{
divide_check_finally check_finally;
if (theDivisor == 0)
throw division_by_zero();
}
return theNumerator / theDivisor;
}
Dans ce cas, le code du destructeur de ''check_finally'' sera exécuté avant de propagé l'exception ou avant d'exécuter la division.