User Tools

Site Tools


in204:tds:sujets:td7:part3

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
Next revision
Previous revision
in204:tds:sujets:td7:part3 [2020/10/20 11:18]
bmonsuez [Question n° 3.3]
in204:tds:sujets:td7:part3 [2022/11/18 10:48] (current)
Line 1: Line 1:
-====== Partie III – Algorithmes génériques======+====== Partie III –  ​Augmenter l’expressivité des exceptions======
  
-Dans la conclusion du cours, nous affirmons qu’un intérêt du polymorphisme,​ c’est de pouvoir écrire un algorithme « générique », c’est à dire un algorithme qui pourra fonctionner pour des objets représentant des données de plusieurs types.+[[in204:​tds:​sujets:​td7|TD8]]
  
-Nous considérons un algorithme ​de tri très simple ​qui fonctionne sur des entiers ​:+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 ​:
  
 <code cpp> <code cpp>
-void insertion_sort(std::​vector<int>& anArray) +#include<exception
-+...
- for (int i = 0; i < anArray.size(); i++) +
-+
- for (int j = i + 1; j < anArray.size(); j++) +
-+
- if (anArray[i] > anArray[j]) +
- std::​swap(anArray[i],​ anArray[j]);​ +
-+
-+
-+
-</​code>​+
  
-Cet algorithme fonctionne uniquement pour des entiers. Nous nous proposons de transformer cette fonction afin qu’elle puisse aussi bien trier des entiers mais aussi des réels ou des complexes. +class extended_exception ​public ​std::runtime_error
- +
-===== Question n° 1===== +
- +
-Pour ce faire, nous concevons une classe « abstraite » ayant comme nom « Base » qui expose les fonctions nécessaires à l’écriture de l’algorithme de tri.  +
- +
-Nous souhaitons utiliser l’algorithme avec la signature suivante ​: +
- +
-<code cpp> +
-void insertion_sort(std::vector<​Base*>&​ anArray) +
-</​code>​ +
- +
-Quelles sont les méthodes virtuelles que la classe abstraite doit exposer ?  +
- +
-<hidden Correction>​ +
- +
-La classe doit exposée une méthode permettant de déterminer si un élément est plus grand que l'​autre. Il peut s'agit soit d'une méthode testant si un élément est plus grand que l'​autre : +
- +
-<code cpp> +
-class Base+
 { {
-public:  +  ​public: 
-    ​virtual bool isGreater(const Base&const = 0;  +    ​void catched(); 
-        // Retourne true si l'objet courant est plus grand. +      // 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.
 }; };
 </​code>​ </​code>​
  
-Nous pouvons ajouter éventuellement des méthodes complémentaires comme : +===== Question ​n°1 ===== 
- +
-<code cpp> +
-    virtual bool isGreaterOrEqual(const Base&) const = 0;  +
-    virtual bool isLess(const Base&) const = 0;  +
-    virtual bool isLessOrEqual(const Base&) const = 0;  +
-    virtual bool isEqual(const Base&) const = 0;  +
-    virtual bool isNotEqual(const Base&) const = 0;  +
-</​code>​ +
- +
-Voire éventuellement une fonction ''​compareTo'':​ +
- +
-<code cpp> +
-    virtual int compareTo(const Base&) const = 0; +
-       // retourne -1 si l'​instance courante est plus petite que l'​instance passée en paramètre +
-       // retourne 0 si elles sont égales +
-       // retourne 1 si l'​instance courante est plus grande que l'​instance passée en paramètre. +
-</​code>​ +
- +
-</​hidden>​ +
- +
- +
-===== Question ​n° 2===== +
- +
-==== Question n° 2.1==== +
- +
-Ajouter à la classe de base ''​Base''​ une méthode purement virtuelle ''​print()''​ qui affiche le contenu de la classe sur la console.+
  
 +Proposer une implantation de cette classe. ​
  
 <hidden Correction>​ <hidden Correction>​
  
-Il suffit d'​ajouter la méthode suivante: 
  
 +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.
 <code cpp> <code cpp>
-class Base+#​include<​exception>​ 
 +... 
 +  
 +class extended_exception:​ public std::​runtime_error
 { {
-    ...+private: 
 +    private unsigned m_catched = 0;    ​
 public: public:
-    ​..+    ​void catched() { m_catched ++; } 
-    ​virtual void print() const = 0+      // 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. 
 +};
 </​code>​ </​code>​
-</​hidden>​ 
- 
-==== Question n° 2.2 ==== 
- 
-Créer une fonction ''​print(std::​vector<​Base*>​ anArray)''​ qui prend un tableau en paramètre et affiche l’ensemble des éléments contenus dans le tableau sur la console. 
- 
- 
-<hidden Correction>​ 
- 
-Il suffit d'​énumérer les éléments dans le tableau ou dans le containeur. 
- 
-Nous pouvons faire une version orientée tableau qui ressemblera à cette version. 
- 
-void print(std::​vector<​Base*>​ anArray) 
-{ 
-    std::cout << "​[";​ 
-    int lastIndex = anArray.size()-1;​ 
-    if(lastIndex >= 0) 
-    { 
-        for(int i = 0; i < lastIndex; i++) 
-        { 
-            anArray[i]->​print(); ​ 
-            std::cout << ", "; 
-        } 
-        anArray[lastIndex]->​print(); ​ 
-    } 
-    std::cout << "​]";​ 
-} 
- 
-Si nous souhaitons être un peu plus "​C++"​ dans l'​esprit,​ nous accéderons par des itérateurs. 
  
 +Il est nécessaire de définir les constructeurs pour cette classe :
 <code cpp> <code cpp>
- +class extended_exception:​ public std::​runtime_error
-template<​typename iterator>​ +
-void print(iterator theStart, iterator theEnd)+
 { {
-    std::cout << "​[";​ +... 
-    ​if (theStart != theEnd) +public
-    { +    ​explicit extended_exception(const std::​string&​ aMessage) 
-        ​*theStart->​print(); +        ​runtime_error(aMessage
-        ​theStart++;​ +    {} 
-        ​while ​(theStart != theEnd+    ​explicit extended_exception(const char* aMessage) 
-        ​+        ​runtime_error(aMessage
-            std::cout << ", "; +    ​{
-            *theStart->​print()+...
-        } +
-        std::cout << "​]";​ +
-    } +
-}+
 </​code>​ </​code>​
  
 </​hidden>​ </​hidden>​
  
-===== Question ​n° 3 =====+===== Question ​n°2 ===== 
  
-===== Question n° 3.1 ==== +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''​.
- +
-Créer ​une classe ​Entier qui dérive de la classe « abstraite » ''​Base'' ​et qui contient un champ ''​m_value'' ​ayant comme type le type ''​int''​.+
  
 +Réaliser une implantation de la classe extended_divide_by_zero.
  
 <hidden Correction>​ <hidden Correction>​
  
-En plus de contenir le champ entier ​''​m_value''​la classe ​doit définir le constructeur par défaut, le constructeur de conversion d'une valeur de type ''​int''​ vers l'​objet ''​Entier''​ ainsi que la conversion d'un objet ''​Entier''​ vers une valeur de type ''​int''​.+ 
 +La nouvelle classe ​''​extended_divide_by_zero'' ​dérive directement de la classe ''​extended_exception''​. ​Ce qui nous donne le code suivant :
  
 <code cpp> <code cpp>
-class Entier ​: public ​Base+class extended_divide_by_zero: public ​extended_exception
 { {
-private: +public:  
-    int m_value; +    ​division_by_zero(): extended_exception("​Division by zero"
-public: +    {}
-    ​Entier() : m_value(0{} +
-    ​Entier(int aValue): m_value(aValue) ​{+
-    operator int() { return m_value; } +
- +
-    void print() const { std::cout << m_value; } +
-    bool isGreater(const Base& unEntier) const +
-    { +
-        return m_value > dynamic_cast<​const Entier&>​(unEntier).m_value;​ +
-    ​}+
 }; };
 </​code>​ </​code>​
  
-En plus, il faut définir les fonctions ''​print''​ et ''​isGreater''​. La fonction ''​print''​ se contente d'​afficher le contenu de l'​entier sur la console, la fonction ''​isGreater''​ est plus complexe. En effet, l'​argument a comme type un type ''​Base''​ et non pas un type ''​Entier''​. Il va falloir convertir ce type vers un type ''​Entier''​ qui est un type hérité, c'est pour cela que nous faisons un ''​dynamic_cast<​const Entier&>​(unEntier)''​ qui va essayer de convertir si le paramètre ''​unEntier''​ hérite bien de ''​Entier''​ ou est un ''​Entier''​. Sinon, un exception ''​std::​bad_cast''​ sera lancé si la conversion n'est pas possible. 
-  
 </​hidden>​ </​hidden>​
  
-==== Question ​n° 3.2 ====+===== 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''​.
  
-Créer une fonction ​qui crée un tableau de ''​theNumberOfValues''​ entiers de type Entier+Nous souhaitons tester cette nouvelle ​fonction ​avec le code suivant :
  
 <code cpp> <code cpp>
-std::​vector<​Base*>​ create_integer_array( +double successive_division(double i) nexcept(false)
- size_t theNumberOfValues) +
-</​code>​ +
-qui crée un tableau de ''​theNumberOfValues''​ valeurs commençant par la valeur ''​0''​ et se terminant à la valeur ''​theNumberOfValues-1''​.+
  
- +void test_succesive_division() noexcept
-<hidden Correction>​ +
- +
-<code cpp> +
-std::​vector<​Base*>​ create_integer_array(size_t theNumberOfValues)+
 { {
-    ​std::vector<Base*result+ double i; 
-    ​result.resize(theNumberOfValues); + std::cout << "The numerator: "; 
-    ​for ​(int i = 0; i < theNumberOfValues;​ i+++ std::​cin ​>> i
-        ​result[i] = new Entier(i); + try { 
-    ​return result;+ successive_division(i); 
 +
 + catch ​(extended_division_by_zero e{ 
 + e.catched(); 
 + std::cout << "​Division by zero occurred after "  
 + << e.getCatchNumber() 
 + << " divisions"​ << std::endl; 
 + }
 } }
-</​code>​ 
- 
-</​hidden>​ 
- 
-==== Question n° 3.3 ==== 
- 
-Créer une fonction qui crée un tableau de ''​theNumberOfValues''​ entiers de type ''​Entier''​ dont les éléments sont des nombres aléatoires compris entre ''​theMinValue''​ et ''​theMaxValue''​. 
  
-Cette fonction peut s’implanter comme suit.  +double successive_division(double i)  
-<code cpp>  + noexcept(false)
-std::​vector<​Base*>​ create_random_integer_array( +
- size_t theNumberOfValues,​ int theMinValue,​ int theMaxValue)+
 { {
- std::​random_device rd+  double j
- std::mt19937 gen(rd());  +  std::cout << "Next divisor ​(-1 to stop sequence): "
- std::uniform_int_distribution<​distr(theMinValue,​ theMaxValue); +  std::cin >> j; 
- std::​vector<​Base*>​ array(theNumberOfValues); +  if (j == -1) 
- for (size_t ​= 0; i < theNumberOfValuesi+++ return i
- array[i] = new Entier(distr(gen)); +  try { 
- return array;+ 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; 
 +  }
 } }
 </​code>​ </​code>​
 +
 +Commentez le résultat de l’exécution.
  
 <hidden Correction>​ <hidden Correction>​
-La fonction initialise un générateur de nombre aléatoire ainsi qu'une distribution uniforme ​ 
-pour des entiers entre ''​theMinValue''​ et ''​theMaxValue''​. Elle utilise ensuite ce générateur 
-couplé à la distribution pour initialiser les ''​theNumberOfValues''​ éléments du tableau. 
  
-</​hidden>​ +Si nous effectuons un appel à la fonction avec la séquence de chiffres suivante : 
-===== Question n°4 =====+''​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.
  
-Modifier ​la fonction de tri ''​insertion_sort(std::​vector<​int>&​ anArray)''​ afin de ne plus travailler sur des entiers mais sur des objets dérivant de la classe « abstraite » ''​Base'' ​:+Ceci est effectivement logique, puisque quand nous effectuons ​la capture ​:
  
 <code cpp> <code cpp>
-void insertion_sort(std::​vector<​Base*>&​ anArray)+  try { 
 +    successive_division(j)
 +    return divide(i,​j);​ 
 +  } 
 +  catch(extended_divide_by_zero e) { 
 +    e.catched() ;  
 +    throw e;  
 +  } 
 +  ...
 </​code>​ </​code>​
  
-Tester ​la fonction avec le tableau d’entiers ​que vous avez créé à la question n°3.3.+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.
  
-===== Question n° 5 ===== +Cependant, nous pouvons éviter ​de recopier l'​exception en effectuant un passage non pas par recopie mais par référence :
-Nous souhaitons étendre le code à une classe ​de réel.+
  
-==== Question n° 5.1 ==== 
- 
-Ecrire une nouvelles classe ''​Reel''​ qui dérive de ''​Base''​ et qui contient un champ  ''​m_value''​ de type ''​double''​. 
- 
-==== Question n° 5.2 ==== 
- 
-Créer une fonction qui crée un tableau de ''​theNumberOfValues''​ objets de type ''​Reel''​ dont les éléments sont des nombres aléatoires compris entre ''​theMinValue''​ et ''​theMaxValue''​. 
- 
-Cette fonction peut s’implanter comme suit.  
-  
 <code cpp> <code cpp>
-std::​vector<​Base*>​ create_random_double_array( +  try 
- size_t theNumberOfValues,​ double theMinValue,​ double theMaxValue) +    ​successive_division(j); 
-+    ​return divide(i,j); 
- std::​random_device rd; +  } 
- std::​mt19937 gen(rd()); +  catch(extended_divide_by_zero&​ e{ 
- std::​uniform_real_distribution<>​ distr(theMinValuetheMaxValue); +    ​e.catched() ;  
- std::​vector<​Base*>​ array(theNumberOfValues);​ +    throw e;  
- for (size_t i = 0; i < theNumberOfValues;​ i+++  } 
- array[i] = new Real(distr(gen)); +  ...
- return array+
-}+
 </​code>​ </​code>​
  
-Vérifier que l’algorithme de tri fonctionne aussi pour cette nouvelle nouvelle classe.+Dans ce cas, l'​exception n'est pas recopiée et supprimée mais modifiée et ensuite propagée. 
 + 
 +</​hidden>​
  
in204/tds/sujets/td7/part3.1603192707.txt.gz · Last modified: 2020/10/20 11:18 by bmonsuez