User Tools

Site Tools


in204:tds:sujets:td7:part4

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)

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
}

Correction

Correction

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 ?

Correction

Correction

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.

Correction

Correction

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.

in204/tds/sujets/td7/part4.txt · Last modified: 2022/11/18 10:48 (external edit)