This shows you the differences between two versions of the page.
Both sides previous revision Previous revision Next revision | Previous revision | ||
in204:tds:sujets:td2:part2 [2019/09/23 17:02] bmonsuez [Question n°1] |
in204:tds:sujets:td2:part2 [2022/11/18 10:49] (current) |
||
---|---|---|---|
Line 48: | Line 48: | ||
}; | }; | ||
+ | </code> | ||
+ | |||
+ | Ceci permet de simplifier le code de la fonction ''testMyBiDiCounter'' en supprimant les appels aux fonctions ''setMax'' et ''reset''. | ||
+ | |||
+ | <code cpp> | ||
+ | |||
+ | void testMyBiDiCounter() | ||
+ | { | ||
+ | MyBiDiCounter counterA(4); | ||
+ | counterA.print(); | ||
+ | for(int i=0; i < 6; i++) | ||
+ | { | ||
+ | counterA.increment(); | ||
+ | counterA.print(); | ||
+ | } | ||
+ | for(int i=0; i < 6; i++) | ||
+ | { | ||
+ | counterA.decrement(); | ||
+ | counterA.print(); | ||
+ | } | ||
+ | } | ||
</code> | </code> | ||
Line 58: | Line 79: | ||
En fait, nous pouvons définir une famille de compteur : | En fait, nous pouvons définir une famille de compteur : | ||
- | * Le compteur ForwardCounter qui compte de 0 à max et repars à 0. | + | * Le compteur ''ForwardCounter'' qui compte de 0 à max et repars à 0. |
- | * Le compteur BackwardCounter qui compte max à 9 et repars à max. | + | * Le compteur ''BackwardCounter'' qui compte max à 9 et repars à max. |
- | * Et le compteur BiDiCounter qui peut incrémenter ou décrémenter le compteur interne. | + | * Et le compteur ''BiDiCounter'' qui peut incrémenter ou décrémenter le compteur interne. |
Nous souhaitons partager le maximum de code entre ces différents compteurs. Une solution consiste à définir l’arbre de dérivation suivant : | Nous souhaitons partager le maximum de code entre ces différents compteurs. Une solution consiste à définir l’arbre de dérivation suivant : | ||
Line 71: | Line 92: | ||
Faites la liste des méthodes, champs pouvant être partagés et la liste des méthodes et champs propres à chacune des classes. | Faites la liste des méthodes, champs pouvant être partagés et la liste des méthodes et champs propres à chacune des classes. | ||
+ | |||
+ | <hidden Correction> | ||
+ | L'ensemble des méthodes de ''MyCounter'' doivent être présentes dans la classes ''BaseCounter'' à l'exception de la méthode ''increment''. | ||
+ | |||
+ | En effet, ces méthodes sont communes aux trois classes ''ForwardCounter'', ''ReverseCounter'' et ''BiDiCounter''. | ||
+ | |||
+ | </hidden> | ||
=== Question n°2.2 === | === Question n°2.2 === | ||
Implanter la classe ''BaseCounter''. On s’inspirera fortement de la classe ''MyCounter'' déjà définie. | Implanter la classe ''BaseCounter''. On s’inspirera fortement de la classe ''MyCounter'' déjà définie. | ||
+ | |||
+ | <hidden Correction> | ||
+ | |||
+ | Une proposition pour la classes ''BaseCounter'' serait la classe suivante : | ||
+ | |||
+ | <code cpp> | ||
+ | #ifndef COUNTER_HPP | ||
+ | #define COUNTER_HPP | ||
+ | |||
+ | #include<iostream> | ||
+ | |||
+ | class BaseCounter | ||
+ | { | ||
+ | protected: | ||
+ | unsigned counter; | ||
+ | unsigned max; | ||
+ | |||
+ | public: | ||
+ | unsigned getCounter() const { return counter; } | ||
+ | unsigned getMax() const { return max; } | ||
+ | void reset() { counter = 0; } | ||
+ | void set(unsigned value) { counter = value; } | ||
+ | void setMax(unsigned value) | ||
+ | { | ||
+ | max = value; | ||
+ | if(value > counter) | ||
+ | counter = counter % max; | ||
+ | } | ||
+ | |||
+ | protected: | ||
+ | BaseCounter(): counter(0), max(0) {} | ||
+ | BaseCounterunsigned theCounter, | ||
+ | unsigned theMax): counter(theCounter), max(theMax) | ||
+ | {} | ||
+ | BaseCounterunsigned (const BaseCounterunsigned & anotherCounter): | ||
+ | counter(anotherCounter.counter), | ||
+ | max(anotherCounter.max) | ||
+ | {} | ||
+ | ~BaseCounterunsigned () | ||
+ | {} | ||
+ | }; | ||
+ | </code> | ||
+ | |||
+ | Nous supposons que la classe ''BaseCounter'' n'est qu'une pure classe de base, c'est-à-dire qu'aucun objet de type ''BaseCounter'' sera créé. Pour éviter de pouvoir créer un objet de type ''BaseCounter'', nous modifions la visibilité des constructeurs de constructeurs ''public'' en constructeur ''protected''. Désormais, les constructeurs ne pourront être appelés que par des classes qui dérivent de la classe ''BaseCounter'', interdisant ainsi la possibilité de créer accidentellement un objet de type ''BaseCounter''. | ||
+ | |||
+ | </hidden> | ||
+ | |||
=== Question n°2.3 === | === Question n°2.3 === | ||
Line 80: | Line 155: | ||
Implanter les classes ''ForwardCounter'', ''BackwardCounter'' et ''BiDiCounter'' qui héritent chacune de la classe ''BaseCounter''. | Implanter les classes ''ForwardCounter'', ''BackwardCounter'' et ''BiDiCounter'' qui héritent chacune de la classe ''BaseCounter''. | ||
- | === Question n°3 === | + | <hidden Correction 1 - Héritage Simple> |
+ | |||
+ | |||
+ | |||
+ | Cette correction propose de créer trois classes ''ForwardCounter'', ''BackwardCounter'' et ''BiDiCounter'' qui héritent de la classe ''BaseCounter'' en l'étendant avec les fonctions manquantes : | ||
+ | * ''increment'' pour la classe ''ForwardCounter'', | ||
+ | * ''decrement'' pour la classe ''BackwardCounter'', | ||
+ | * ''increment'' et ''decrement'' pour la classe ''BiDiCounter''. | ||
+ | |||
+ | Le code correspondant à cette solution est le suivant : | ||
+ | |||
+ | <code cpp> | ||
+ | class ForwardCounter: public BaseCounter | ||
+ | { | ||
+ | public: | ||
+ | void increment() | ||
+ | { | ||
+ | if(counter < max) | ||
+ | counter = counter + 1; | ||
+ | else | ||
+ | counter = 0; | ||
+ | } | ||
+ | |||
+ | ForwardCounter(): BaseCounter() {} | ||
+ | ForwardCounter(const ForwardCounter& aCounter): BaseCounter(aCounter) {} | ||
+ | explicit ForwardCounter(unsigned theMaxValue): ForwardCounter(0, theMaxValue) {} | ||
+ | ForwardCounter(unsigned theCounter, unsigned theMaxValue): BaseCounter(theCounter, theMaxValue) {} | ||
+ | }; | ||
+ | |||
+ | class BackwardCounter: public BaseCounter | ||
+ | { | ||
+ | public: | ||
+ | void decrement() | ||
+ | { | ||
+ | if(counter > 0) | ||
+ | counter = counter -1; | ||
+ | else | ||
+ | counter = max; | ||
+ | } | ||
+ | BackwardCounter(): BaseCounter() {} | ||
+ | BackwardCounter(const BackwardCounter& aCounter): BaseCounter(aCounter) {} | ||
+ | explicit BackwardCounter(unsigned theMaxValue): BackwardCounter(0, theMaxValue) {} | ||
+ | BackwardCounter(unsigned theCounter, unsigned theMaxValue): BaseCounter(theCounter, theMaxValue) {} | ||
+ | }; | ||
+ | |||
+ | class BiDiCounter: public BaseCounter | ||
+ | { | ||
+ | public: | ||
+ | void increment() | ||
+ | { | ||
+ | if(counter < max) | ||
+ | counter = counter + 1; | ||
+ | else | ||
+ | counter = 0; | ||
+ | } | ||
+ | void decrement() | ||
+ | { | ||
+ | if(counter > 0) | ||
+ | counter = counter -1; | ||
+ | else | ||
+ | counter = max; | ||
+ | } | ||
+ | |||
+ | BiDiCounter(): BaseCounter() {} | ||
+ | BiDiCounter(const BiDiCounter& aCounter): BaseCounter(aCounter) {} | ||
+ | explicit BiDiCounter(unsigned theMaxValue): ForwardCounter(0, theMaxValue) {} | ||
+ | BiDiCounter(unsigned theCounter, unsigned theMaxValue): BaseCounter(theCounter, theMaxValue) {} | ||
+ | }; | ||
+ | |||
+ | </code> | ||
+ | |||
+ | </hidden> | ||
+ | |||
+ | <hidden Correction 2 - Héritage Simple en factorisant l'implantation des fonctions ''increment'' et ''decrement''> | ||
+ | |||
+ | Dans l'exemple précédent, nous constatons que nous définissons deux fois le code pour la fonction ''increment'' et la fonction ''decrement'', ce qui conduit à dupliquer le code et surtout à devoir si jamais nous trouvons une erreur dans une des fonctions membres ''increment'' (resp. ''decrement'') de devoir penser à corriger la deuxième implantation de la fonction membre ''increment'' (resp. ''decrement''). | ||
+ | |||
+ | |||
+ | Dans ce cas, il est possible d'implanter les deux fonctions ''%%__increment%%'' et ''%%__decrement%%'' qui sont des fonctions internes à la classe et accessibles uniquement des classes dérivées. Ces fonctions contiennent le code de ''increment'' et de ''decrement'', nous les avons préfixés par ''%%__%%'' pour bien indiquer qu'il s'agit de fonction interne. Elles seront donc déclarées comme ''protected'' dans la classe ''BaseCounter'' qui désormais s'écrira comme suit : | ||
+ | |||
+ | <code cpp> | ||
+ | class BaseCounter | ||
+ | { | ||
+ | protected: | ||
+ | unsigned counter; | ||
+ | unsigned max; | ||
+ | |||
+ | public: | ||
+ | unsigned getCounter() const { return counter; } | ||
+ | unsigned getMax() const { return max; } | ||
+ | void reset() { counter = 0; } | ||
+ | void set(unsigned value) { counter = value; } | ||
+ | void setMax(unsigned value) | ||
+ | { | ||
+ | max = value; | ||
+ | if(value > counter) | ||
+ | counter = counter % max; | ||
+ | } | ||
+ | |||
+ | protected: | ||
+ | BaseCounter(): counter(0), max(0) {} | ||
+ | BaseCounterunsigned theCounter, | ||
+ | unsigned theMax): counter(theCounter), max(theMax) | ||
+ | {} | ||
+ | BaseCounter (const BaseCounterunsigned & anotherCounter): | ||
+ | counter(anotherCounter.counter), | ||
+ | max(anotherCounter.max) | ||
+ | {} | ||
+ | ~BaseCounterunsigned () | ||
+ | {} | ||
+ | |||
+ | void __increment() | ||
+ | { | ||
+ | if(counter < max) | ||
+ | counter = counter + 1; | ||
+ | else | ||
+ | counter = 0; | ||
+ | } | ||
+ | void __decrement() | ||
+ | { | ||
+ | if(counter > 0) | ||
+ | counter = counter -1; | ||
+ | else | ||
+ | counter = max; | ||
+ | } | ||
+ | }; | ||
+ | </code> | ||
+ | |||
+ | Il suffit désormais d'ajouter aux classes ''ForwardCounter'' et ''BiDiCounter'' la fonction : | ||
+ | |||
+ | <code cpp> | ||
+ | public: | ||
+ | void increment() { __increment(); } | ||
+ | </code> | ||
+ | |||
+ | ainsi qu'aux classes ''BackwardCounter'' et ''BiDiCounter'' la fonction :: | ||
+ | |||
+ | <code cpp> | ||
+ | public: | ||
+ | void decrement() { __decrement(); } | ||
+ | </code> | ||
+ | |||
+ | Ce qui nous donne le code suivant pour les trois classes dérivées : | ||
+ | |||
+ | <code cpp> | ||
+ | class ForwardCounter: public BaseCounter | ||
+ | { | ||
+ | public: | ||
+ | void increment() { __increment(); } | ||
+ | |||
+ | ForwardCounter(): BaseCounter() {} | ||
+ | ForwardCounter(const ForwardCounter& aCounter): BaseCounter(aCounter) {} | ||
+ | explicit ForwardCounter(unsigned theMaxValue): ForwardCounter(0, theMaxValue) {} | ||
+ | ForwardCounter(unsigned theCounter, unsigned theMaxValue): BaseCounter(theCounter, theMaxValue) {} | ||
+ | }; | ||
+ | |||
+ | class BackwardCounter: public BaseCounter | ||
+ | { | ||
+ | public: | ||
+ | void decrement() { __decrement(); } | ||
+ | |||
+ | BackwardCounter(): BaseCounter() {} | ||
+ | BackwardCounter(const BackwardCounter& aCounter): BaseCounter(aCounter) {} | ||
+ | explicit BackwardCounter(unsigned theMaxValue): BackwardCounter(0, theMaxValue) {} | ||
+ | BackwardCounter(unsigned theCounter, unsigned theMaxValue): BaseCounter(theCounter, theMaxValue) {} | ||
+ | }; | ||
+ | |||
+ | class BiDiCounter: public BaseCounter | ||
+ | { | ||
+ | public: | ||
+ | void increment() { __increment(); } | ||
+ | void decrement() { __decrement(); } | ||
+ | |||
+ | BiDiCounter(): BaseCounter() {} | ||
+ | BiDiCounter(const BiDiCounter& aCounter): BaseCounter(aCounter) {} | ||
+ | explicit BiDiCounter(unsigned theMaxValue): ForwardCounter(0, theMaxValue) {} | ||
+ | BiDiCounter(unsigned theCounter, unsigned theMaxValue): BaseCounter(theCounter, theMaxValue) {} | ||
+ | }; | ||
+ | |||
+ | </code> | ||
+ | |||
+ | </hidden> | ||
+ | |||
+ | <hidden Correction 3 - Héritage Multiples> | ||
+ | |||
+ | A priori, ''BiDiCounter'' a besoin à la fois de la méthode ''increment'' qui est défini dans ''ForwardCounter'' et de la méthode ''decrement'' qui est défini dans ''BackwardCounter''. Il serait tentant de dire, puisque C++ supporte l'héritage multiple, que ''BiDiCounter'' hérite à la fois de ''ForwardCounter'' et de ''BackwardCounter''. | ||
+ | |||
+ | Cependant, nous allons avoir quelques soucis, puisque ''ForwardCounter'' hérite de ''BaseCounter'' et donc possède : | ||
+ | * une propre instance des champs ''counter'' et ''max'' qui sont définis dans la classe de base ''BaseCounter'' que je vais noter ''ForwardCounter''->''BaseCounter''->''counter'' et ''ForwardCounter''->''BaseCounter''->''max'' | ||
+ | * des fonctions ''getCounter'', ''getMax'', ''reset'' ''set'', ''setMax'' qui vont modifier les champs précédents ''ForwardCounter''->''BaseCounter''->''counter'' et ''ForwardCounter''->''BaseCounter''->''max'', nous allons identifier ces fonctions par leur chemin d'accès ''ForwardCounter''->''BaseCounter''->''getCounter'', ... | ||
+ | * une fonctions ''increment'' qui va modifier le champ ''ForwardCounter''->''BaseCounter''->''counter''. | ||
+ | |||
+ | |||
+ | De même la classe ''BackwardCounter'' hérite de ''BaseCounter'' est possède : | ||
+ | * une propre instance des champs ''counter'' et ''max'' qui sont définis dans la classe de base ''BaseCounter'' que je vais noter ''BackwardCounter''->''BaseCounter''->''counter'' et ''BackwardCounter''->''BaseCounter''->''max'' | ||
+ | * des fonctions ''getCounter'', ''getMax'', ''reset'' ''set'', ''setMax'' qui vont modifier les champs précédents ''BackwardCounter''->''BaseCounter''->''counter'' et ''BackwardCounter''->''BaseCounter''->''max'', nous allons identifier ces fonctions par leur chemin d'accès ''BackwardCounter''->''BaseCounter''->''getCounter'', ... | ||
+ | * une fonctions ''decrement'' qui va modifier le champ ''BackwardCounter''->''BaseCounter''->''counter''. | ||
+ | |||
+ | Ceci signifie que nous avons deux instances des champs ''max'' et ''counter'' en fonction de l'héritage : | ||
+ | * soit ''ForwardCounter''->''BaseCounter''->''max'' ou ''BackwardCounter''->''BaseCounter''->''max'', | ||
+ | * soit ''ForwardCounter''->''BaseCounter''->''counter'' ou ''BackwardCounter''->''BaseCounter''->''counter''. | ||
+ | |||
+ | Plus embêtant, la fonction ''increment'' modifie le champ ''ForwardCounter''->''BaseCounter''->>''counter'', la fonction ''decrement'' modifier le champ ''BackwardCounter''->''BaseCounter''->>''counter'', ce qui fait que la fonction ''increment'' ne pourra jamais travailler de concert avec la fonction ''decrement'' puisqu'elles ne partagent pas les mêmes variables. | ||
+ | |||
+ | De fait, il n'est pas possible de faire hériter naivement ''BiDiCounter'' des classes ''ForwardCounter'' et ''BackwardCounter'' puisque nous nous retrouvons avec le schéma d'héritage suivant : | ||
+ | |||
+ | |||
+ | La solution serait que ''ForwardCounter'' et ''BackwardCounter'' ne crée pas chacun une instance de ''BaseCounter'' mais hérite de la même instance de ''BaseCounter''. Le schéma d'héritage deviendrait alors celui-ci : | ||
+ | |||
+ | Pour ce faire, les classes ''ForwardCounter'' et ''BackwardCounter'' vont faire référence à une même instance de la classe de base qui sera crée par l'objet qui héritera des classes ''ForwardCounter'' et ''BackwardCounter''. | ||
+ | |||
+ | Pour ce faire, nous déclarons toujous la classe ''BaseCounter'' comme précédemment. Par contre, nous mettons le mot-clé ''virtual'' pour indiquer que la classe dérivant de la classe de base ''BaseCounter'' fait référence à une classe unique qui peut-être partagée. | ||
+ | |||
+ | <code cpp> | ||
+ | class ForwardCounter: public virtual BaseCounter | ||
+ | { | ||
+ | public: | ||
+ | void increment() | ||
+ | { | ||
+ | if(counter < max) | ||
+ | counter = counter + 1; | ||
+ | else | ||
+ | counter = 0; | ||
+ | } | ||
+ | |||
+ | ForwardCounter(): BaseCounter() {} | ||
+ | ForwardCounter(const ForwardCounter& aCounter): BaseCounter(aCounter) {} | ||
+ | explicit ForwardCounter(unsigned theMaxValue): ForwardCounter(0, theMaxValue) {} | ||
+ | ForwardCounter(unsigned theCounter, unsigned theMaxValue): BaseCounter(theCounter, theMaxValue) {} | ||
+ | }; | ||
+ | |||
+ | |||
+ | class BackwardCounter: public virtual BaseCounter | ||
+ | { | ||
+ | public: | ||
+ | void decrement() | ||
+ | { | ||
+ | if(counter > 0) | ||
+ | counter = counter -1; | ||
+ | else | ||
+ | counter = max; | ||
+ | } | ||
+ | BackwardCounter(): BaseCounter() {} | ||
+ | BackwardCounter(const ForwardCounter& aCounter): BaseCounter(aCounter) {} | ||
+ | explicit BackwardCounter(unsigned theMaxValue): BackwardCounter(0, theMaxValue) {} | ||
+ | BackwardCounter(unsigned theCounter, unsigned theMaxValue): BaseCounter(theCounter, theMaxValue) {} | ||
+ | }; | ||
+ | |||
+ | </code> | ||
+ | |||
+ | Apparemment, rien n'a changé par rapport au code qui a été donné pour la première solution proposée pour la question à l'exception du mot-clé ''virtual''. Cependant, le comportement est complètement différent. | ||
+ | |||
+ | Quand nous créons un objet ''class ForwardCounter: public BaseCounter'', je vais allouer une mémoire qui va correspondre à l'espace mémoire requis pour la classe ''BaseCounter'' ainsi que la mémoire requise pour l'extension ''ForwardCounter''. | ||
+ | |||
+ | Quand nous créons un objet de type ''class ForwardCounter: public virtual BaseCounter'', la création dépend du fait que nous créons l'objet ''ForwardCounter'' ou un objet qui dérive de ''ForwardCounter''. | ||
+ | |||
+ | * Si l'objet créé a pour type ''ForwardCounter''.\\ Considérons le code de création suivant :\\ <code cpp> | ||
+ | ForwardCounter counter(0, 5); | ||
+ | </code>\\ le constructeur correspondant est : <code cpp> | ||
+ | ForwardCounter(unsigned theCounter, unsigned theMaxValue): BaseCounter(theCounter, theMaxValue) {} | ||
+ | </code>\\ la création de l'objet est décomposé en deux phases : | ||
+ | - Allocation et initialisation d'un objet ''BaseCounter'' qui est initialisé en appelant le constructeur tel que défini par ''ForwardCounter'', c'est à dire ''BaseCounter(0, 5)'', | ||
+ | - Allocation et initialisation de l'objet ''ForwardCounter'' qui contient un espace mémoire correspondant à la mémoire requise pour les données internes de ''ForwardCounter'' (en l'espèce aucun champs n'est à allouer ni initialiser) ainsi qu'une référence à l'objet de type ''BaseCounter'' qui a été précédemment créé. \\ Nous voyons bien que ''ForwardCounter'' ne contient plus d'objet ''BaseCounter'' en son sein mais fait référence à un objet ''BaseCounter'' externe à l'objet ''ForwardCounter''. | ||
+ | |||
+ | * Si l'objet créé a pour type un type dérivé comme par exemple ''BiDiCounter''.\\ Considérons le code de création suivant :\\ <code cpp> | ||
+ | BiDiCounter counter(5); | ||
+ | </code>\\ le constructeur correspondant est : \\ <code cpp> | ||
+ | BiDiCounter(unsigned theCounter, unsigned theMaxValue): | ||
+ | ForwardCounter(), BackwardCounter(), | ||
+ | BaseCounter(theCounter, theMaxValue) {} | ||
+ | </code> \\ la création de l'objet est décomposé en trois phases : | ||
+ | - Allocation et initialisation d'un objet ''BaseCounter'' qui est initialisé en appelant le constructeur tel que défini par ''BiDiCounter'', c'est à dire ''BaseCounter(0, 5)'', | ||
+ | - Allocation et initialisation de l'objet ''BiDiCounter'' qui contient une instance de ''ForwardCounter'' ainsi qu'une instance de ''BackwardCounter'' en allouant la mémoire nécessaire pour ''ForwardCounter'', ''BackwardCounter'' ainsi que les définitions propres à ''BiDiCounter''. Les constructeurs ''ForwardCounter()'' ainsi que ''BackwardCounter()''.\\ Attention, lorsque le constructeur ''ForwardCounter'' (resp. ''BackwardCounter'') est appellé par la classe dérivant de ''ForwardCounter'' (resp. ''BackwardCounter'') pour initialiser que ''ForwardCounter'' (resp. ''BackwardCounter'') et non pas la classe de base ''BaseCounter''. La partie relative à ''BaseCounter'' dans la déclaration du constructeur <code cpp> | ||
+ | ForwardCounter(): BaseCounter() {} | ||
+ | </code> est ignorée. Cette partie n"est active que quand on crée un objet de type ''ForwardCounter'' (resp. ''BackwardCounter'') et non pas un objet dérivé de ''ForwardCounter'', (resp ''BackwardCounter''). | ||
+ | - Affectation à chacun des instances de ''ForwardCounter'' et de ''BackwardCounter'' d'une référence à l'objet ''BaseCounter'' qui a été créé. | ||
+ | |||
+ | Ce processus garantit que les instances ''ForwardCounter'' et ''BackwardCounter'' feront référence à une seule et unique instance ''BaseCounter''. | ||
+ | |||
+ | De ce fait, le code pour la classe ''BiDiCounter'' devient le suivant : | ||
+ | <code cpp> | ||
+ | class BiDiCounter: public ForwardCounter, public BackwardCounter | ||
+ | { | ||
+ | public: | ||
+ | BiDiCounter(): ForwardCounter(), BackwardCounter() {} | ||
+ | BiDiCounter(const BiDiCounter& aCounter): | ||
+ | ForwardCounter(aCounter), | ||
+ | BackwardCounter((const BackwardCounter&)aCounter), | ||
+ | BaseCounter(aCounter) {} | ||
+ | BiDiCounter(unsigned theMaxValue): BiDiCounter(0, theMaxValue) {} | ||
+ | BiDiCounter(unsigned theCounter, unsigned theMaxValue): | ||
+ | ForwardCounter(), | ||
+ | BackwardCounter(), | ||
+ | BaseCounter(theCounter, theMaxValue) {} | ||
+ | }; | ||
+ | </code> | ||
+ | |||
+ | Ceci permet de comprendre que l'héritage multiple est intéressant conceptuellement, puisqu'il permet d'hériter de plusieurs comportements. Cependant, sa mise en oeuvre est relativement simple pour des cas où les classes dont on hérite ne dérivent pas d'une même classe de base. Si c'est le cas, il faut recourir à un héritage faisant référence aux classes qui est nettement moins intuitif et peut même conduire à des erreurs. En effet, il est nécessaire de créer l'instance qui sera partagée entre les différentes classes et à défaut de création explicite, s'il existe un constructeur par défaut, C++ utilisera ce constructeur par défaut pour initialiser l'objet partagé, même si ce n'était pas votre souhait mais simplement un oubli de votre part. | ||
+ | |||
+ | </hidden> | ||
+ | |||
+ | ===== Question n°3 ===== | ||
Tester le comportement de vos compteurs à partir du code suivant | Tester le comportement de vos compteurs à partir du code suivant | ||
Line 106: | Line 482: | ||
} | } | ||
</code> | </code> | ||
+ | |||
+ | <hidden Correction> | ||
+ | Il suffit de tester les codes et de s'assurer du bon fonctionnement. | ||
+ | </hidden> | ||