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