====== Partie III – Augmenter l’expressivité des exceptions======
[[in204:tds:sujets:td7|TD8]]
Nous souhaitons créer une nouvelle classe d’exception qui dérive de la classe exception et qui compte le nombre de fois qu’une exception est capturée.
Nous proposons que cette exception extended_exception dérive de la classe de base std::exception et fournisse en complément des méthodes offertes par la classe de base les méthodes suivantes :
#include
...
class extended_exception : public std::runtime_error
{
public:
void catched();
// Est appelé chaque fois que l’on souhaite indiqué à la classe qu’elle a été
// capturée.
int getCatchNumber() const;
// Retourne le nombre de fois que l’exception a été capturée.
};
===== Question n°1 =====
Proposer une implantation de cette classe.
Nous proposons d'ajouter un compteur qui est incrémenté à chaque fois que l'exception a été capturée, ie. que la méthode ''catched'' a été appellée. Par défaut ce compteur est initialisé à zéro.
#include
...
class extended_exception: public std::runtime_error
{
private:
private unsigned m_catched = 0;
public:
void catched() { m_catched ++; }
// Est appelé chaque fois que l’on souhaite indiqué à la classe qu’elle a été
// capturée.
int getCatchNumber() const { return m_catched; }
// Retourne le nombre de fois que l’exception a été capturée.
};
Il est nécessaire de définir les constructeurs pour cette classe :
class extended_exception: public std::runtime_error
{
...
public:
explicit extended_exception(const std::string& aMessage):
runtime_error(aMessage)
{}
explicit extended_exception(const char* aMessage):
runtime_error(aMessage)
{}
...
===== Question n°2 =====
Nous proposons de créer une classe exception ''extended_divide_by_zero'' qui se comporte comme la classe ''divide_by_zero'' mais qui dérive de la classe ''extended_exception''.
Réaliser une implantation de la classe extended_divide_by_zero.
La nouvelle classe ''extended_divide_by_zero'' dérive directement de la classe ''extended_exception''. Ce qui nous donne le code suivant :
class extended_divide_by_zero: public extended_exception
{
public:
division_by_zero(): extended_exception("Division by zero")
{}
};
===== Question n°3 =====
Nous proposons de modifier la fonction ''divide'' pour qu’elle lance non plus une exception ''divide_by_zero'' mais ''extended_divide_by_zero''.
Nous souhaitons tester cette nouvelle fonction avec le code suivant :
double successive_division(double i) nexcept(false)
void test_succesive_division() noexcept
{
double i;
std::cout << "The numerator: ";
std::cin >> i;
try {
successive_division(i);
}
catch (extended_division_by_zero e) {
e.catched();
std::cout << "Division by zero occurred after "
<< e.getCatchNumber()
<< " divisions" << std::endl;
}
}
double successive_division(double i)
noexcept(false)
{
double j;
std::cout << "Next divisor (-1 to stop sequence): ";
std::cin >> j;
if (j == -1)
return i;
try {
successive_division(j);
return divide(i,j);
}
catch(division_by_zero e)
{
throw extended_division_by_zero();
}
catch (extended_division_by_zero e)
{
e.catched();
throw e;
}
}
Commentez le résultat de l’exécution.
Si nous effectuons un appel à la fonction avec la séquence de chiffres suivante :
''20 4 4 1 3 0 3 6 -1'', nous allons avoir 5 captures de l'exception de type ''extended_divide_by_zero''. Cependant, nous allons avoir comme message que l'exception s'est produite après ''5'' divisions. Ce qui est conforme.
Ceci est effectivement logique, puisque quand nous effectuons la capture :
try {
successive_division(j);
return divide(i,j);
}
catch(extended_divide_by_zero e) {
e.catched() ;
throw e;
}
...
l'argument de la clause ''catch'' est ''extended_divide_by_zero'', ce qui signifie que nous **dupliquons** l'argument. Nous créons une copie de l'ancienne exception qui ensuite sera détruite. Ensuite, nous augmentons la valeur du champ ''m_catched'' qui a été recopié. En conséquence, nous comptons bien le nombre de fois que l'exception a été capturée à partir du moment où elle a été générée.
Cependant, nous pouvons éviter de recopier l'exception en effectuant un passage non pas par recopie mais par référence :
try {
successive_division(j);
return divide(i,j);
}
catch(extended_divide_by_zero& e) {
e.catched() ;
throw e;
}
...
Dans ce cas, l'exception n'est pas recopiée et supprimée mais modifiée et ensuite propagée.