This shows you the differences between two versions of the page.
| Next revision | Previous revision | ||
|
in204:tds:sujets:td7:part3 [2019/11/03 12:51] bmonsuez created |
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> | ||
| + | |||
| + | ===== Question n°1 ===== | ||
| + | |||
| + | Proposer une implantation de cette classe. | ||
| + | |||
| + | <hidden 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. | ||
| + | <code cpp> | ||
| + | #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> | ||
| + | |||
| + | 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 ===== | ||
| + | |||
| + | 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. | ||
| + | |||
| + | <hidden Correction> | ||
| + | |||
| + | |||
| + | La nouvelle classe ''extended_divide_by_zero'' dérive directement de la classe ''extended_exception''. Ce qui nous donne le code suivant : | ||
| + | |||
| + | <code cpp> | ||
| + | class extended_divide_by_zero: public extended_exception | ||
| + | { | ||
| + | public: | ||
| + | division_by_zero(): extended_exception("Division by zero") | ||
| + | {} | ||
| + | }; | ||
| + | </code> | ||
| + | |||
| + | </hidden> | ||
| + | |||
| + | ===== 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 : | ||
| + | |||
| + | <code cpp> | ||
| + | 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; | ||
| + | } | ||
| } | } | ||
| </code> | </code> | ||
| + | Commentez le résultat de l’exécution. | ||
| - | 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. | + | <hidden Correction> |
| - | ===== Question n° 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. | ||
| - | 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. | + | 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> | ||
| + | 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 : | ||
| + | |||
| + | <code cpp> | ||
| + | try { | ||
| + | successive_division(j); | ||
| + | return divide(i,j); | ||
| + | } | ||
| + | catch(extended_divide_by_zero& e) { | ||
| + | e.catched() ; | ||
| + | throw e; | ||
| + | } | ||
| + | ... | ||
| + | </code> | ||
| + | |||
| + | Dans ce cas, l'exception n'est pas recopiée et supprimée mais modifiée et ensuite propagée. | ||
| + | |||
| + | </hidden> | ||