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