Table of Contents

Partie III – Augmenter l’expressivité des exceptions

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<exception>
...
 
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.

Correction

Correction

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<exception>
...
 
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.

Correction

Correction

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.

Correction

Correction

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.