User Tools

Site Tools


in204:tds:sujets:td7:part1

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:part1 [2020/10/20 07:30]
bmonsuez [Question n° 4]
in204:tds:sujets:td7:part1 [2022/11/18 10:48] (current)
Line 1: Line 1:
-====== Partie I – Surcharge ​des compteurs ​======+====== Partie I – Manipulation ​des Exceptions======
  
-Nous repartons à partir du code des compteurs que nous avons effectué lors de la première séance. ​+[[in204:​tds:​sujets:​td7|TD7]]
  
-Vous pouvez téléchargé le contenu ​des fichiers ​+===== Question n°1 : Gestion simple ​des exceptions en C++ =====
  
 +==== Question n°1.1 ==== 
  
-  - [[in204:​tds:​sujets:​td7:​counter_hpp|Counter.hpp]],+Implanter le code suivant dans un nouveau projet
  
-vous permettant d’avoir accès au code qui correspondrait normalement à ce qui avait été réalisé à la fin de la seconde séance [[in204:​tds:​sujets:​td2|TD2]].+<code cpp>
  
-Vous pouvez tout autant repartir de votre code si vous l’avez archivé.+#include <​iostream>​
  
-===== Question n° 1 ======+double divide(double a, double b); 
 +void test_divide();​
  
-Ajouter à la classe ''​BaseCounter''​ deux nouvelles méthodes virtuelles pures ''​next()''​resp. ''​next(unsigned)''​. Ces deux méthodes purement virtuelles ont pour objet d’appeler les méthodes par défaut pour passer à la valeur suivante. ​+void test_divide() 
 +
 +  double ij; 
 +  for(;;
 +    std::cout << "Le numerateur (0 pour arreter): "; 
 +    std::cin >> i; 
 +    if(i == 0) 
 +      break; 
 +    std::cout << " Le denominateur : "; 
 +    std::cin >> j; 
 +    std::cout << "​Resultat:​ " << divide(i,j) << std::​endl;​ 
 +  }  
 +}
  
-Typiquementpour un compteur qui compte, ce seront les méthodes ''​increment()''​ et ''​increment(unsigned)''​Pour un compte qui décompte, ce seront les méthodes ''​decrement()''​ et ''​decrement(unsigned)''​.+double divide(double adouble b) 
 +
 +  try { 
 +    if(!bthrow b;  
 +  } 
 +  catch (double b
 +    std::cout << "Ne peut pas diviser par zero.\n"; 
 +    return b; 
 +  } 
 +  return a/b; 
 +
 + 
 +void main() 
 +
 +    test_divide() 
 +
 + 
 +</​code>​ 
 + 
 +==== Question n°1.2 ====  
 + 
 +Procéder à une exécution et regarder ce qui se passe quand une division par 0 se produit.
  
 <hidden Correction>​ <hidden Correction>​
  
-Il suffit ​d'ajouter dans la section ''​public''​ de la classe ''​BaseCounter''​ les deux méthodes suivantes : +Lors d'une division par zéro se produit, le code 
 <code cpp> <code cpp>
-class BaseCounter +    std::cout << "Ne peut pas diviser par zero.\n"
-+    ​return b;
-... +
-public: +
-... +
-    virtual void next() = 0+
-    ​virtual void next(unsigned) = 0; +
-... +
-} +
 </​code>​ </​code>​
 +est exécuté et la fonction retourne ''​0''​. Ceci signifie que l'​exception de type ''​double''​ ayant pour valeur ''​0''​ a été générée et a été capturée par la clause ''​catch(double)''​.
  
-Les méthodes sont déclarées comme virtuelles, ce qui signifie qu'une entrée est définie dans la table des méthodes virtuelles. Cependant, comme nous ne définissons la méthode mais uniquement sa déclaration,​ nous mettons le pointeur qui pointe sur le code de la méthode dans la table des méthodes virtuelles à zéro (ou à ''​NULL''​).+<code
 +try { 
 +    if(!bthrow b;  
 +  } 
 +  catch (double b) { 
 +    std::cout << "Ne peut pas diviser par zero.\n"; 
 +    return b; 
 +  } 
 +</​code>​
  
-Ceci à deux conséquences : +Dans les autres casc'est bien le résultat ​de la division ​qui est retournée par la fonction.
-  * d'une partil n'y a pas besoin de fournir un code associé aux méthodes, ce sont les classes dérivées qui devront ​le définir, +
-  * d'​autre part, aucune instance ​de la classe ne pourra être créée, puisqu'​il existe une méthode ​qui n'est pas définie, ie. un pointeur de la table des méthodes virtuelles qui n'est pas initialisé.+
  
 </​hidden>​ </​hidden>​
-===== Question ​n° 2 ======+==== Question ​n°1.3 ​==== 
  
-Ajouter aux classes ''​ForwardCounter'' ​et ''​BackwardCounter'',​ deux nouvelles méthodes ''​next()'',​ resp. ''​next(unsigned)''​. Cette méthode ''​next()''​ (resp. ''​next(unsigned)''​) appellera la méthode ''​increment()'',​ (resp. ''​increment(unsigned)''​) pour la classe ''​ForwardCounter''​. cette méthode ''​next()''​ (resp. ''​next(unsigned)''​) appellera la méthode ''​decrement()'',​ resp. ''​decrement(unsigned)''​ pour la classe ''​BackwardCounter''​.+Exécuter en mode débogage ​et placer un point d’arrêt sur le code de capture de l’exception.
  
 <hidden Correction>​ <hidden Correction>​
-Pour la classe ''​ForwardCounter'', ​il suffit ​de redéfinir les méthodes virtuelles ​''​next()''​ et ''​next(unsigned)''​ comme suit : +L'objectif de cette question est de vous faire manipuler vos outils pour utiliser l'environnement de dévogage de votre environnement de développement. Bien entenduen fonction ​de votre environnement,​ vous avez des procédures différentes pour positionner un point d'arrêt
-<code cpp> +
-class ForwardCounter:​ BaseCounter +
-+
-    ... +
-public: +
-    ... +
-    virtual void next()  +
-    { +
-        increment();​ +
-    } +
-    virtual void next(unsigned aNumberOfSteps) +
-    { +
-        while(aNumberOfSteps-- > 0) +
-            increment();​ +
-    } +
-    ... +
-};     +
-</​code> ​+
  
-et par analogie ​pour la classe ​''​BackwardCounter''​ :+Pour mémoire, voici quelques références ​pour placer des points d'arrêts sur les différents outils (sans garantie aucune d'exhaustivité).
  
-<code cpp> +  * [[https://code.visualstudio.com/​docs/​cpp/​cpp-debug|Debug C++ in Visual Studio Code]]
-class BackwardCounter:​ BaseCounter +
-+
-    ​..+
-public: +
-    ... +
-    virtual void next() +
-    { +
-        decrement();​ +
-    } +
-    virtual void next(unsigned aNumberOfSteps) +
-    { +
-        while (aNumberOfSteps-- > 0) +
-            decrement();​ +
-    } +
-    ... +
-};     +
-</​code> ​+
  
-Il se pose la question de la classe ''​BiDiCounter''​En effet, ''​BiDiCounter''​ hérite à la fois de ''​ForwardCounter''​ mais aussi de ''​BackwardCounter''​ dans le cas de l'​héritage multipleDonc nous avons deux fonctions ''​next()''​ qui sont candidates pour la redéfinition de la fonction ''​next()''​ de la classe de base ''​BaseCounter''​Dans ce cas, nous devons rédéfinir de manière non ambigue la fonction ''​next()''​ et devons ainsi écrire ​:+  * [[http://​wiki.codeblocks.org/index.php/​Debugging_with_Code::​Blocks|Debugging with Code::Blocks]]
  
-<code cpp> +  * [[https://docs.microsoft.com/​fr-fr/​visualstudio/​debugger/​quickstart-debug-with-cplusplus?​view=vs-2019|QuickstartDebug with C++ using the Visual Studio debugger]]
-class BiDiCounterBaseCounter +
-+
-    ​..+
-public: +
-    ... +
-    virtual void next() +
-    { +
-        ForwardCounter::​next();​ +
-    } +
-    virtual void next(unsigned aNumberOfSteps) +
-    { +
-        ForwardCounter::​next(aNumberOfSteps);​ +
-    } +
-    ... +
-};     +
-</​code> ​+
  
-qui appelle dès lors l'​implantation fournie par la classe ''​ForwardCounter''​.+  * [[https://​medium.com/​yay-its-erica/​xcode-debugging-with-breakpoints-for-beginners-5b0d0a39d711|Xcode Debugging with Breakpoints (for Beginners)]]
  
 +  * [[https://​www.cs.swarthmore.edu/​~newhall/​unixhelp/​howto_gdb.php|gdb (and ddd) Guide]]
  
 +  * [[https://​www.emacswiki.org/​emacs/​DebuggingWithEmacs|Debugging with Emacs]]
 </​hidden>​ </​hidden>​
-===== Question ​n° 3 ======+===== Question ​n°2 : Création d’une classe d’exception ​=====
  
-==== Question n° 3.1 =====+Nous envisageons désormais faire les choses correctement. Nous souhaitons définir une classe exception qui dérive de la classe [[https://​en.cppreference.com/​w/​cpp/​error/​exception|std::​exception]] se trouvant définie dans le fichier d'​entête [[https://​en.cppreference.com/​w/​cpp/​header/​exception|<​exception>​]]. Plus spécifiquement,​ nous souhaitons la faire dériver de la classe ​ [[https://​en.cppreference.com/​w/​cpp/​error/​runtime_error|std::​runtime_error]] qui elle-même dérive de [[https://​en.cppreference.com/​w/​cpp/​error/​exception|std::​exception]]
  
-Ajouter un opérateur ​''​operator <<'' ​pour afficher un compteur de type ''​BaseCounter''​ dans le flux.+Cette classe devra s’appeler ​''​division_by_zero''​.
  
 +==== Question n°2.1 ====
  
-<hidden Correction>​+Créer la classe ''​division_by_zero''​. Elle pourra être définie dans un fichier d’entête ''​math.hpp''​ qui contiendra aussi l’entête de la fonction ''​divide''​. Le fichier associé ''​math.cpp''​ contiendre la code de la fonction divide.
  
-<code cpp> +Penser à fournir un message d’erreur cohérent.
-#​include<​iostream>​ +
-... +
-class BaseCounter +
-+
-protected:​ +
-    ... +
-    friend std::​basic_ostream<​charT,​ charTraits>&​ operator << ( +
-        std::​basic_ostream<​charT,​ charTraits>&,​ const BaseCounter&​ aCounter);​ +
-};+
  
-template<class charT, class charTraits +<hidden Correction>
-std::​basic_ostream<​charT,​ charTraits>&​ operator << (std::​basic_ostream<​charT,​ charTraits>&​ aStream, +
-    const BaseCounter&​ aCounter) +
-+
-    aStream << aCounter.counter << "/"​ << aCounter.max;​ +
-    return aStream; +
-}+
  
-</​code>​ +Nous nous proposons de créer un fichier ''​math.hpp''​ qui a le contenu suivant :
-</​hidden>​+
  
-==== Question n° 3.2 =====+<code cpp> 
 +#ifndef mathHPP 
 +#define mathHPP
  
-Ecrire une fonction qui prendre comme paramètre une référence à la classe ''​BaseCounter''​ et qui fait appel à la méthode ''​next''​ un certain nombre de fois. +#​include<​exception>​ 
 +#​include<​stdexcept>​
  
-Tester cette fonction avec des objets instances de ''​ForwardCounter''​ et ''​BackwardCounter''​ dont le code ressemble à la fonction suivante. +class division_by_zero:​ public std::​runtime_error
- +
- +
-<​code>​ +
-testNext(BaseCounter&​ aCounter) +
-    for i = 1  to 10 +
-        aCounter.next();​ +
-        print aCounter +
-    end +
-</​code>​ +
-  +
-<hidden Correction>​ +
- +
-Définissez dans le fichier ''​Counter.cpp''​ une fonction ''​testNext''​ qui correspond au code suivant  +
-<code cpp> +
-void testNext(BaseCounter&​ aCounter)+
 { {
-    for(int i; i < 10; i++) +public: ​ 
-    ​+    ​division_by_zero(): std::runtime_error("​Division by zero") 
-        aCounter.next()+    ​{
-        std::cout << aCounter << ​std::endl; +};
-    } +
-} +
-</​code>​+
  
-Définissez dans le fichier ''​Counter.hpp''​ le prototype de la fonction : +double divide(double a, double b);  
- +#endif
-<code cpp> +
-void testNext(BaseCounter&​)+
 </​code>​ </​code>​
  
-Et enfin dans le fichier ''​main.cpp'' ​:+Ainsi que le fichier ''​math.cpp'' ​qui contient le code de la fonction ''​divide''​.
  
 <code cpp> <code cpp>
 +#include "​math.hpp"​
  
-int main()+double divide(double a, double b
 { {
-    ForwardCounter forward(10);​ +  try { 
-    ​BackwardCounter backward(7); +    ​if(!bthrow b;  
-     +  } 
-    ​testNext(forward); +  ​catch ​(double b
-    ​testNext(backward)+    std::cout << "Ne peut pas diviser par zero.\n"​
-   +    ​return b
 +  } 
 +  return a/b; 
 +
 </​code>​ </​code>​
 </​hidden>​ </​hidden>​
  
-===== Question ​n° 4 ======+==== Question ​n°2.2 ​====
  
-Nous souhaitons que les classes dérivées modifient le comportement de la méthode ​''​print''​.  +Modifier ​les fonctions ​''​divide''​ et ''​test_divide''​ pour prendre ne plus lancer et capturer une exception de type ''​double'' ​mais de type ''​division_by_zero''​.
-Par exemple ​pour ''​ForwardCounter''​, nous souhaitons avoir l’affichage suivant :+
  
-<code> +<hidden Correction>
-template<​class charT, class traits> print(std::​basic_ostream<​charT,​ traits>&​) +
-    affiche "​ForwardCounter : " counter "/"​ max (retour à la ligne) +
-</code>+
  
-**ATTENTION** Il n'est pas possible de créer un patron de méthodes virtuelles. En fait, il faut modifier la méthode ''​print''​ de ''​BaseCounter''​ pourqu'​elle récupère le nom de la classe effective, par exemple en appelant une méthode ''​getClassName''​ qui retourne le nom de la classe effective, soit ''​BaseCounter''​ pour la classe ''​BaseCounter'',​ ''​ForwardCounter''​ pour la classe ''​ForwardCounter''​ et ainsi de suite. +La fonction modifiée s'écrit dorénavant comme suit :
- +
-Modifier en conséquence les classes ''​BaseCounter'',​ ''​ForwardCounter'',​ ''​BackwardCounter''​ et ''​BidirectionalCounter''​. +
- +
-===== Question n° 5 ====== +
- +
-Nous souhaitions modifier le comportement des compteurs.  +
- +
-Actuellement,​ lorsque la valeur du compteur de ForwardCounter atteint la valeur max, il recommence à compter à partir de la valeur minimale. +
- +
-Nous aimerions pouvoir rendre ce comportement « adaptable », c’est-à-dire que nous ne souhaitons pas modifier les méthodes ''​increment(…)''​ mais simplement le comportement quand la valeur maximale est atteinte et ce pour toutes les méthodes ''​increment(…)''​. +
- +
-Pour ce faire, nous suggérons de modifier la méthode ''​increment()''​ de la manière suivante : +
-  * ''​counter''​ est plus petit que ''​max''​ alors incrémente ''​counter''​. +
-  * ''​counter''​ est égal à ''​max''​ la méthode appelle une méthode virtuelle ''​reachMaximum()''​ qui décide ce qu’il faut faire lorsque la valeur maximale du compteur est atteinte. +
- +
-Modifier ''​ForwardCounter''​ afin d’implanter le comportement précédemment décrit. +
- +
-===== Question n° 6 ====== +
- +
-Dériver de la classe ''​ForwardCounter''​ une classe ''​VerboseForwardCounter''​ qui lorsque la valeur maximale affiche un message avant de remettre la valeur du compteur à 0. +
- +
-Vérifier le bon comportement de ''​VerboseForwardCounter''​ avec une fonction de test de type :+
  
 <code cpp> <code cpp>
-void testForwardCounter(const ForwardCounter&​ aCounter)+inline double divide(double a, double b)
 { {
-    for(int i = 0 ; i < 10 ; i+++  try { 
-        ​aCounter.increment();+    if(b == 0)  
 +        ​throw division_by_zero();  
 +  } 
 +  catch (division_by_zero) { 
 +    std::cout << "Ne peut pas diviser par zero.\n";​ 
 +    return 0; 
 +  } 
 +  return a/b;
 } }
 </​code>​ </​code>​
- 
-===== Question n° 7 ====== 
- 
-** A faire en dehors de la séance de TD ** 
- 
- 
-Actuellement,​ lorsque la valeur du compteur de ''​BackwardCounter''​ atteint la valeur ''​m_min'',​ il recommence à compter à partir de la valeur maximale. 
- 
-Nous aimerions pouvoir rendre ce comportement « adaptable », c’est-à-dire que nous ne souhaitons pas modifier les méthodes ''​decrement(…)''​ mais simplement le comportement quand la valeur minimale est atteinte et ce pour toutes les méthodes ''​decrement(…)''​. 
- 
-Pour ce faire, nous suggérons de modifier la méthode ''​decrement()''​ de la manière suivante : 
-  * ''​m_counter''​ est plus petit que ''​m_max''​ alors incrémente ''​m_counter''​. 
-  * ''​m_counter''​ est égal à ''​m_max''​ la méthode appelle une méthode virtuelle ''​reachMinimum()''​ qui décide ce qu’il faut faire lorsque la valeur maximale du compteur est atteinte. 
- 
-Modifier ''​BackwardCounter''​ afin d’implanter le comportement précédemment décrit. 
- 
-===== Question n° 8 ====== 
- 
-** A faire en dehors de la séance de TD ** 
- 
-Modifier la classe ''​BidirectionnalCounter''​ pour rendre son comportement « adaptable », c'​est-à-dire que nous pouvons spécifier comment le compteur continue de compter lorsque la valeur maximale et la valeur minimale sont atteintes. 
- 
-===== Question n° 9 ====== 
- 
-** A faire en dehors de la séance de TD ** 
- 
-Supprimmer tous les fonctions ''​increment''​ et ''​decrement''​ dans les classes ''​BaseCounter'',​ ''​ForwardCounter'',​ ''​BackwardCounter''​ et ''​BidirectionnalCounter''​ et remplacer les par les opérateurs ''​operator ++()'',​ ''​operator ++(int)'',​ ''​operator += (unsigned)''​ et si nécessaire les opérateurs ''​operator --()'',​ ''​operator --(int)''​ et ''​operator -=(unsigned)''​. 
  
 +</​hidden>​
  
in204/tds/sujets/td7/part1.1603179001.txt.gz · Last modified: 2020/10/20 07:30 by bmonsuez