User Tools

Site Tools


in204:tds:sujets:td7:part1

This is an old revision of the document!


Partie I – Surcharge des compteurs

Nous repartons à partir du code des compteurs que nous avons effectué lors de la première séance.

Vous pouvez téléchargé le contenu des fichiers

vous permettant d’avoir accès au code qui correspondrait normalement à ce qui avait été réalisé à la fin de la seconde séance TD2.

Vous pouvez tout autant repartir de votre code si vous l’avez archivé.

Question n° 1

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.

Typiquement, pour 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).

Correction

Correction

Il suffit d'ajouter dans la section public de la classe BaseCounter les deux méthodes suivantes :

class BaseCounter
{
...
public:
...
    virtual void next() = 0;
    virtual void next(unsigned) = 0;
...
};  

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

Ceci à deux conséquences :

  • d'une part, il 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é.

Question n° 2

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.

Correction

Correction

Pour la classe ForwardCounter, il suffit de redéfinir les méthodes virtuelles next() et next(unsigned) comme suit :

class ForwardCounter: BaseCounter
{
    ...
public:
    ...
    virtual void next() 
    {
        increment();
    }
    virtual void next(unsigned aNumberOfSteps)
    {
        while(aNumberOfSteps-- > 0)
            increment();
    }
    ...
};    

et par analogie pour la classe BackwardCounter :

class BackwardCounter: BaseCounter
{
    ...
public:
    ...
    virtual void next()
    {
        decrement();
    }
    virtual void next(unsigned aNumberOfSteps)
    {
        while (aNumberOfSteps-- > 0)
            decrement();
    }
    ...
};    

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 multiple. Donc 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 :

class BiDiCounter: BaseCounter
{
    ...
public:
    ...
    virtual void next()
    {
        ForwardCounter::next();
    }
    virtual void next(unsigned aNumberOfSteps)
    {
        ForwardCounter::next(aNumberOfSteps);
    }
    ...
};    

qui appelle dès lors l'implantation fournie par la classe ForwardCounter.

Question n° 3

Question n° 3.1

Ajouter un opérateur operator « pour afficher un compteur de type BaseCounter dans le flux.

Correction

Correction

#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> 
std::basic_ostream<charT, charTraits>& operator << (std::basic_ostream<charT, charTraits>& aStream,
    const BaseCounter& aCounter)
{
    aStream << aCounter.counter << "/" << aCounter.max;
    return aStream;
}

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.

Tester cette fonction avec des objets instances de ForwardCounter et BackwardCounter dont le code ressemble à la fonction suivante.

testNext(BaseCounter& aCounter)
    for i = 1  to 10
        aCounter.next();
        print aCounter
    end

Correction

Correction

Définissez dans le fichier Counter.cpp une fonction testNext qui correspond au code suivant

void testNext(BaseCounter& aCounter)
{
    for(int i; i < 10; i++)
    {
        aCounter.next();
        std::cout << aCounter << std::endl;
    }
}

Définissez dans le fichier Counter.hpp le prototype de la fonction :

void testNext(BaseCounter&)

Et enfin dans le fichier main.cpp :

int main()
{
    ForwardCounter forward(10);
    BackwardCounter backward(7);
 
    testNext(forward);
    testNext(backward);
}    

Question n° 4

L’ancienne méthode print() est définie au niveau de la classe de base BaseCounter. Nous souhaitons la redéfinir par une méthode virtuelle :

    template<class charT, class traits> 
    void print(std::basic_ostream<charT, traits>& aStream)
    {
        aStream << "Counter" << counter << "/" << max << std::endl;
    }

Ajouter la surcharge de l'opérateur operator « appelant la méthode print précédemment définie.

template<class charT, class traits> operator << (std::basic_ostream<charT, traits>& aStream, 
    const BaseCounter& aBaseCounter)
{
    aBaseCounter.print(aStream);
    return aStream;
}

Nous souhaitons que les classes dérivées modifient le comportement de la méthode print. Par exemple pour ForwardCounter, nous souhaitons avoir l’affichage suivant :

template<class charT, class traits> print(std::basic_ostream<charT, traits>&)
    affiche "ForwardCounter : " counter "/" max (retour à la ligne)

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.

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 :

void testForwardCounter(const ForwardCounter& aCounter)
{
    for(int i = 0 ; i < 10 ; i++)
        aCounter.increment();
}

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

in204/tds/sujets/td7/part1.1603178910.txt.gz · Last modified: 2020/10/20 07:28 by bmonsuez