This shows you the differences between two versions of the page.
| Both sides previous revision Previous revision Next revision | Previous revision | ||
|
in204:tds:sujets:td7:part3 [2019/11/03 13:30] bmonsuez |
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> |
| + | ... | ||
| + | |||
| + | class extended_exception : public std::runtime_error | ||
| { | { | ||
| - | for (int i = 0; i < anArray.size(); i++) | + | public: |
| - | { | + | void catched(); |
| - | for (int j = i + 1; j < anArray.size(); j++) | + | // Est appelé chaque fois que l’on souhaite indiqué à la classe qu’elle a été |
| - | { | + | // capturée. |
| - | if (anArray[i] > anArray[j]) | + | int getCatchNumber() const; |
| - | std::swap(anArray[i], anArray[j]); | + | // Retourne le nombre de fois que l’exception a été capturée. |
| - | } | + | }; |
| - | } | + | |
| - | } | + | |
| </code> | </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. | + | ===== Question n°1 ===== |
| - | ===== Question n° 1===== | + | Proposer une implantation de cette classe. |
| - | 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. | + | <hidden Correction> |
| - | Nous souhaitons utiliser l’algorithme avec la signature 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> | ||
| - | void insertion_sort(std::vector<Base*>& anArray) | + | #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. | ||
| + | }; | ||
| </code> | </code> | ||
| - | Quelles sont les méthodes virtuelles que la classe abstraite doit exposer ? | + | Il est nécessaire de définir les constructeurs pour cette classe : |
| + | <code cpp> | ||
| + | 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) | ||
| + | {} | ||
| + | ... | ||
| + | </code> | ||
| - | + | </hidden> | |
| - | ===== Question n° 2===== | + | |
| - | ==== Question n° 2.1==== | + | ===== Question n°2 ===== |
| - | Ajouter à la classe de base ''Base'' une méthode purement virtuelle ''print()'' qui affiche le contenu de la classe sur la console. | + | 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''. |
| - | ==== Question n° 2.2 ==== | + | Réaliser une implantation de la classe extended_divide_by_zero. |
| - | 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> |
| - | ===== Question n° 3 ===== | ||
| - | ===== Question n° 3.1 ==== | + | La nouvelle classe ''extended_divide_by_zero'' dérive directement de la classe ''extended_exception''. Ce qui nous donne le code suivant : |
| - | 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''. | + | <code cpp> |
| + | class extended_divide_by_zero: public extended_exception | ||
| + | { | ||
| + | public: | ||
| + | division_by_zero(): extended_exception("Division by zero") | ||
| + | {} | ||
| + | }; | ||
| + | </code> | ||
| - | ==== Question n° 3.2 ==== | + | </hidden> |
| - | Créer une fonction qui crée un tableau de ''theNumberOfValues'' entiers de type Entier | + | ===== Question n°3 ===== |
| - | <code cpp> | + | Nous proposons de modifier la fonction ''divide'' pour qu’elle lance non plus une exception ''divide_by_zero'' mais ''extended_divide_by_zero''. |
| - | std::vector<Base*> create_integer_array( | + | |
| - | 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''. | + | |
| - | ==== Question n° 3.3 ==== | + | Nous souhaitons tester cette nouvelle fonction avec le code suivant : |
| - | 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''. | + | <code cpp> |
| + | double successive_division(double i) nexcept(false) | ||
| - | Cette fonction peut s’implanter comme suit. | + | void test_succesive_division() noexcept |
| - | <code cpp> | + | |
| - | std::vector<Base*> create_random_integer_array( | + | |
| - | size_t theNumberOfValues, int theMinValue, int theMaxValue) | + | |
| { | { | ||
| - | std::random_device rd; | + | double i; |
| - | std::mt19937 gen(rd()); | + | std::cout << "The numerator: "; |
| - | std::uniform_int_distribution<> distr(theMinValue, theMaxValue); | + | std::cin >> i; |
| - | std::vector<Entier*> array(theNumberOfValues); | + | try { |
| - | for (size_t i = 0; i < theNumberOfValues; i++) | + | successive_division(i); |
| - | array[i] = new Entier(distr(gen)); | + | } |
| - | return array; | + | catch (extended_division_by_zero e) { |
| + | e.catched(); | ||
| + | std::cout << "Division by zero occurred after " | ||
| + | << e.getCatchNumber() | ||
| + | << " divisions" << std::endl; | ||
| + | } | ||
| } | } | ||
| - | </code> | ||
| - | ===== Question n°4 ===== | + | double successive_division(double i) |
| - | + | noexcept(false) | |
| - | 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'' : | + | { |
| - | + | double j; | |
| - | <code cpp> | + | std::cout << "Next divisor (-1 to stop sequence): "; |
| - | void insertion_sort(std::vector<Base*>& anArray) | + | 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; | ||
| + | } | ||
| + | } | ||
| </code> | </code> | ||
| - | Tester la fonction avec le tableau d’entiers que vous avez créé à la question n°3.3. | + | Commentez le résultat de l’exécution. |
| - | ===== Question n° 5 ===== | + | <hidden Correction> |
| - | Nous souhaitons étendre le code à une classe de réel. | + | |
| - | ==== Question n° 5.1 ==== | + | 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. | ||
| - | Ecrire une nouvelles classe ''Reel'' qui dérive de ''Base'' et qui contient un champ ''m_value'' de type ''double''. | + | Ceci est effectivement logique, puisque quand nous effectuons la capture : |
| - | ==== Question n° 5.2 ==== | + | <code cpp> |
| + | try { | ||
| + | successive_division(j); | ||
| + | return divide(i,j); | ||
| + | } | ||
| + | catch(extended_divide_by_zero e) { | ||
| + | e.catched() ; | ||
| + | throw e; | ||
| + | } | ||
| + | ... | ||
| + | </code> | ||
| - | 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''. | + | 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 : | ||
| - | 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(theMinValue, theMaxValue); | + | 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> | ||