This shows you the differences between two versions of the page.
Both sides previous revision Previous revision Next revision | Previous revision | ||
in204:tds:sujets:td8:part1 [2020/11/02 16:12] bmonsuez [Question n°2] |
in204:tds:sujets:td8:part1 [2022/11/18 10:49] (current) |
||
---|---|---|---|
Line 1: | Line 1: | ||
- | ====== Création & Manipulation de Processus léger ====== | + | ====== Partie I – Surcharge des compteurs ====== |
- | [[in204:tds:sujets:td8|TD8]] | + | Nous repartons à partir du code des compteurs que nous avons effectué lors de la première séance. |
- | ===== Références===== | + | Vous pouvez téléchargé le contenu des fichiers |
- | [[http://en.cppreference.com/w/cpp/thread/thread|std::thread]] | ||
- | ===== Question n°1 ===== | + | - [[in204:tds:sujets:td8:counter_hpp|Counter.hpp]], |
- | Créer un processus léger qui est associé à une fonction simple. | + | 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]]. |
+ | |||
+ | 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)''. | ||
+ | |||
+ | <hidden Correction> | ||
+ | |||
+ | Il suffit d'ajouter dans la section ''public'' de la classe ''BaseCounter'' les deux méthodes suivantes : | ||
+ | |||
+ | <code cpp> | ||
+ | class BaseCounter | ||
+ | { | ||
+ | ... | ||
+ | public: | ||
+ | ... | ||
+ | virtual void next() = 0; | ||
+ | virtual void next(unsigned) = 0; | ||
+ | ... | ||
+ | }; | ||
+ | </code> | ||
+ | |||
+ | 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é. | ||
+ | |||
+ | </hidden> | ||
+ | ===== 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''. | ||
+ | |||
+ | <hidden Correction> | ||
+ | Pour la classe ''ForwardCounter'', il suffit de redéfinir les méthodes virtuelles ''next()'' et ''next(unsigned)'' comme suit : | ||
+ | <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'' : | ||
+ | |||
+ | <code cpp> | ||
+ | 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 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 : | ||
+ | |||
+ | <code cpp> | ||
+ | class BiDiCounter: BaseCounter | ||
+ | { | ||
+ | ... | ||
+ | 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''. | ||
+ | |||
+ | |||
+ | </hidden> | ||
+ | ===== Question n° 3 ====== | ||
+ | |||
+ | ==== Question n° 3.1 ===== | ||
+ | |||
+ | Ajouter un opérateur ''operator <<'' pour afficher un compteur de type ''BaseCounter'' dans le flux. | ||
+ | |||
+ | |||
+ | <hidden Correction> | ||
<code cpp> | <code cpp> | ||
#include<iostream> | #include<iostream> | ||
- | #include<thread> | + | ... |
+ | class BaseCounter | ||
+ | { | ||
+ | protected: | ||
+ | ... | ||
+ | friend std::basic_ostream<charT, charTraits>& operator << ( | ||
+ | std::basic_ostream<charT, charTraits>&, const BaseCounter& aCounter); | ||
+ | }; | ||
- | void simple_method() | + | template<class charT, class charTraits> |
+ | std::basic_ostream<charT, charTraits>& operator << (std::basic_ostream<charT, charTraits>& aStream, | ||
+ | const BaseCounter& aCounter) | ||
{ | { | ||
- | int i = 5; | + | aStream << aCounter.counter << "/" << aCounter.max; |
- | int x = 10; | + | return aStream; |
- | int result = i * x; | + | |
- | std::cout << "This code calculated the value " | + | |
- | << result << " from thread ID: " | + | |
- | << std::this_thread::get_id() << "\n"; | + | |
} | } | ||
+ | </code> | ||
+ | </hidden> | ||
- | int main() | + | ==== Question n° 3.2 ===== |
+ | |||
+ | 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. | ||
+ | |||
+ | |||
+ | <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) | ||
{ | { | ||
- | std::thread simpleThread(&simple_method); | + | for(int i; i < 10; i++) |
- | std::cout << "Main thread is executing and waiting\n"; | + | { |
- | simpleThread.join(); | + | aCounter.next(); |
- | std::cout << "Alternate thread has terminated.\n"; | + | std::cout << aCounter << std::endl; |
- | return 0; | + | } |
} | } | ||
+ | </code> | ||
+ | |||
+ | Définissez dans le fichier ''Counter.hpp'' le prototype de la fonction : | ||
+ | |||
+ | <code cpp> | ||
+ | void testNext(BaseCounter&) | ||
+ | </code> | ||
+ | |||
+ | Et enfin dans le fichier ''main.cpp'' : | ||
+ | |||
+ | <code cpp> | ||
+ | |||
+ | int main() | ||
+ | { | ||
+ | ForwardCounter forward(10); | ||
+ | BackwardCounter backward(7); | ||
+ | | ||
+ | testNext(forward); | ||
+ | testNext(backward); | ||
+ | } | ||
+ | </code> | ||
+ | </hidden> | ||
+ | |||
+ | ===== Question n° 4 ====== | ||
+ | |||
+ | Nous souhaitons que les classes dérivées modifient le comportement de l'opérateur d'écriture sur le flux. | ||
+ | Par exemple pour ''ForwardCounter'', nous souhaitons avoir l’affichage suivant : | ||
+ | <code> | ||
+ | template<class charT, class traits> std::basic_ostream<charT, traits>(std::basic_ostream<charT, traits>&, BaseCounter& aCounter) | ||
+ | affiche "ForwardCounter : " counter "/" max (retour à la ligne) | ||
</code> | </code> | ||
- | Exécuter le code et analyser la sortie. Commenter celle-ci, notamment au regard de la documentation de la classe [[http://en.cppreference.com/w/cpp/thread/thread|std::thread]]. | + | **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''. | ||
<hidden Correction> | <hidden Correction> | ||
- | Un objet ''std::thread"' correspondant à un processus léger est créé et est associé au code la méthode ''simple_method''. Ce processus démarre immédiatement et lance le calcul. L'exécution du code principal se poursuit et le message ''Main thread is executing and waiting'' est affiché. La ligne suivant attend que l'exécution du processus léger associé à l'objet ''std::thread'' termine. Une fois que ce processus a terminé, l'exécution du processus principal continue, affiche le message ''Alternate thread has terminated'' et rend la main. | + | La solution la plus simple est de définir une méthode virtuelle qui retourne le nom de la classe de l'objet. Cette méthode est définie dans la classe de base ''BaseCounter'' comme une méthode purement virtuelle, ce qui oblige de définir cette méthode dans les classes dérivées. |
+ | |||
+ | L'opération de conversion ''operator <<'' du compteur vers une représentation textuelle appelle la méthode virtuelle exposée par ''BaseCounter'' et redéfinie par les classes dérivant de ''BaseCounter'' pour obtenir le nom de la classe effective. | ||
+ | |||
+ | Ceci a pour conséquence de modifier le code des classes ''BaseCounter'', ''ForwardCounter'', ''BackwardCounter'' et ''BiDiCounter'' ainsi que celui de l'opérateur ''operator << (std::basic_ostream<charT, charTraits>&, const BaseCounter&)'' comme suit : | ||
+ | |||
+ | <code cpp> | ||
+ | |||
+ | class BaseCounter | ||
+ | { | ||
+ | ... | ||
+ | protected: | ||
+ | virtual const char* getClassName() const = 0; | ||
+ | ... | ||
+ | }; | ||
+ | |||
+ | template<class charT, class charTraits> | ||
+ | std::basic_ostream<charT, charTraits>& operator << (std::basic_ostream<charT, charTraits>& aStream, | ||
+ | const BaseCounter& aCounter) | ||
+ | { | ||
+ | aStream << aCounter.getClassName() << ":" << aCounter.counter << "/" << aCounter.max; | ||
+ | return aStream; | ||
+ | } | ||
+ | |||
+ | class ForwardCounter : public virtual BaseCounter | ||
+ | { | ||
+ | protected: | ||
+ | virtual const char* getClassName() const { return "ForwardCounter"; } | ||
+ | ... | ||
+ | }; | ||
+ | |||
+ | <code cpp> | ||
+ | class BackwardCounter : public virtual BaseCounter | ||
+ | { | ||
+ | protected: | ||
+ | virtual const char* getClassName() const { return "BackwardCounter"; } | ||
+ | ... | ||
+ | }; | ||
+ | |||
+ | |||
+ | class BiDiCounter : public ForwardCounter, public BackwardCounter | ||
+ | { | ||
+ | protected: | ||
+ | virtual const char* getClassName() const { return "BiDiCounter "; } | ||
+ | ... | ||
+ | }; | ||
+ | </code> | ||
</hidden> | </hidden> | ||
- | ===== Question n°2 ===== | + | ===== 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. | ||
- | Ecrire un programme qui lance les deux calculs suivants en parallèle, le premier dans un processus léger secondaire, le premier dans le processus léger principal : | + | Vérifier le bon comportement de ''VerboseForwardCounter'' avec une fonction de test de type : |
<code cpp> | <code cpp> | ||
- | void worker_process(int numberOfIterations) | + | void testForwardCounter(const ForwardCounter& aCounter) |
{ | { | ||
- | for (int i = 1; i < numberOfIterations; i++) | + | for(int i = 0 ; i < 10 ; i++) |
- | { | + | aCounter.increment(); |
- | std::cout << "Worker Thread: " << i << "\n"; | + | |
- | } | + | |
} | } | ||
</code> | </code> | ||
- | et | + | <hidden Correction> |
+ | |||
+ | La méthode la plus simple est de transformer la méthode ''increment'' définie dans ''ForwardCounter'' en méthode virtuelle. | ||
<code cpp> | <code cpp> | ||
- | void main_process() | + | |
+ | class ForwardCounter : public virtual BaseCounter | ||
{ | { | ||
- | for (int i = 1; i < 1000; i++) | + | protected: |
- | { | + | virtual const char* getClassName() const { return "ForwardCounter"; } |
- | std::cout << "Primary Thread: " << i << "\n"; | + | |
- | } | + | public: |
- | } | + | void increment() |
+ | { | ||
+ | if (counter < max) | ||
+ | counter = counter + 1; | ||
+ | else | ||
+ | counter = 0; | ||
+ | } | ||
+ | |||
+ | ... | ||
</code> | </code> | ||
- | + | ||
- | Tester le code. | + | Ce qui permet ensuite de redéfinir dans la classe ''VerboseForwardCounter'' comme suit : |
+ | |||
+ | <code cpp> | ||
+ | |||
+ | |||
+ | class VerboseForwardCounter : ForwardCounter | ||
+ | { | ||
+ | public: | ||
+ | void virtual increment() | ||
+ | { | ||
+ | if (counter < max) | ||
+ | counter = counter + 1; | ||
+ | else | ||
+ | { | ||
+ | std::cout << "Maximal value: " << max << " has been reached." << std::endl; | ||
+ | counter = 0; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | VerboseForwardCounter() : ForwardCounter() {} | ||
+ | VerboseForwardCounter(const ForwardCounter& aCounter) : ForwardCounter(aCounter) {} | ||
+ | explicit VerboseForwardCounter(unsigned theMaxValue) : VerboseForwardCounter(0, theMaxValue) {} | ||
+ | VerboseForwardCounter(unsigned theCounter, unsigned theMaxValue) : ForwardCounter(theCounter, theMaxValue) {} | ||
+ | |||
+ | }; | ||
+ | |||
+ | </code> | ||
+ | |||
+ | Dès lors, l'appel de la fonction ''testNext'' retournera bien le message chaque fois que la valeur maximale est atteinte. | ||
+ | |||
+ | |||
+ | </hidden> | ||
+ | ===== Question n° 7 ====== | ||
+ | |||
+ | ** A faire en dehors de la séance de TD ** | ||
+ | |||
+ | |||
+ | Actuellement, lorsque la valeur du compteur de ''BackwardCounter'' atteint la valeur ''0'', 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 : | ||
+ | * ''counter'' est plus grand que ''0'' alors décrémente ''counter''. | ||
+ | * ''counter'' est égal à ''0'' 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. | ||
+ | |||
<hidden Correction> | <hidden Correction> | ||
- | Nous pouvons écrire le code suivant qui crée deux processus légers, le premier exécutant la fonction ''worker_process'' avec comme paramètre ''10000'' et le second exécutant la fonction ''main_proc''. | + | Nous ajoutons une methode virtuelle dans ''BackwardCounter'' dénomée ''reachMinimum()'' qui est appellée par ''decrement()'' lorsque le compteur atteint la valeur zéro. Le comportement par défaut consiste à remettre le compteur à la valeur maximale. Ceci donne le code suivant : |
<code cpp> | <code cpp> | ||
- | int main() | + | class BackwardCounter : public virtual BaseCounter |
{ | { | ||
- | std::thread worker_proc(&worker_process, 10000); | + | protected: |
- | std::thread main_proc(&main_process); | + | virtual const char* getClassName() const { return "BackwardCounter"; } |
- | worker_proc.join(); | + | |
- | main_proc.join(); | + | virtual void reachMinimum() |
- | } | + | { |
+ | counter = max; | ||
+ | } | ||
+ | |||
+ | public: | ||
+ | void decrement() | ||
+ | { | ||
+ | if (counter > 0) | ||
+ | counter = counter - 1; | ||
+ | else | ||
+ | { | ||
+ | reachMinimum(); | ||
+ | } | ||
+ | } | ||
+ | ... | ||
+ | }; | ||
</code> | </code> | ||
- | L'exécution commence par la tâche ''worker_proc'' et nous devons voir certains messages de la tâche ''main_proc'', nous voyons cependant que les messages ne sont pas très entrelacés, ce qui est du au fait que l'on alloue un temps d'utilisation de la console à chacun des deux processus légers. | + | </hidden> |
- | Pour mettre un peu d'entrelacement, nous pouvons ajouter un peu de temps entre les affichaches, par exemple 1ns pour le premier processus et 10ns pour le second. Ceci nous donne le code suivant: | + | ===== Question n° 8 ====== |
- | <code cpp> | + | ** A faire en dehors de la séance de TD ** |
- | #include<iostream> | + | |
- | #include<thread> | + | |
- | #include <chrono> | + | |
+ | Modifier la classe ''BiDiCounter'' 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. | ||
+ | <hidden Correction> | ||
- | void worker_process(int numberOfIterations) | + | De fait, il est plus intéressant de rendre le comportement « adaptable » de à la fois ''ForwardCounter'' et ''BackwardCounter''. ''ForwardCounter''implante la méthode ''reachMaximum'' et ''BackwardCounter'' la méthode ''reachMinimun''. |
+ | |||
+ | class ForwardCounter : public virtual BaseCounter | ||
{ | { | ||
- | for (int i = 1; i < numberOfIterations; i++) | + | protected: |
- | { | + | virtual const char* getClassName() const { return "ForwardCounter"; } |
- | std::cout << "Worker Thread: " << i << "\n"; | + | |
- | std::this_thread::sleep_for(10ns); | + | |
- | } | + | |
- | } | + | |
- | void main_process() | + | virtual void reachMaximum() |
+ | { | ||
+ | counter = 0; | ||
+ | } | ||
+ | |||
+ | public: | ||
+ | void increment() | ||
+ | { | ||
+ | if (counter < max) | ||
+ | counter = counter + 1; | ||
+ | else | ||
+ | reachMaximum(); | ||
+ | } | ||
+ | ... | ||
+ | }; | ||
+ | |||
+ | </code> | ||
+ | </hidden> | ||
+ | |||
+ | ===== 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 Correction> | ||
+ | |||
+ | Nous ajoutons les opérateurs ''++'' à la classe ''ForwardCounter'' et ''--'' à la classe ''BackwardCounter''. Ce qui nous donne les codes suivants : | ||
+ | |||
+ | <code cpp> | ||
+ | class ForwardCounter : public virtual BaseCounter | ||
{ | { | ||
- | for (int i = 1; i < 1000; i++) | + | protected: |
- | { | + | ... |
- | using namespace std; | + | virtual void reachMaximum() |
- | std::cout << "Primary Thread: " << i << "\n"; | + | { |
- | std::this_thread::sleep_for(1ns); | + | counter = 0; |
- | } | + | } |
- | } | + | ... |
+ | public: | ||
+ | ... | ||
+ | ForwardCounter& operator ++() | ||
+ | { | ||
+ | if (counter < max) | ||
+ | counter = counter + 1; | ||
+ | else | ||
+ | reachMaximum(); | ||
+ | return *this; | ||
+ | } | ||
+ | ForwardCounter operator ++(int) | ||
+ | { | ||
+ | ForwardCounter result(*this); | ||
+ | operator++(); | ||
+ | return result; | ||
+ | } | ||
+ | ... | ||
+ | }; | ||
- | int main() | + | class BackwardCounter : public virtual BaseCounter |
{ | { | ||
- | std::thread worker_proc(&worker_process, 10000); | + | protected: |
- | std::thread main_proc(&main_process); | + | ... |
- | worker_proc.join(); | + | virtual void reachMinimum() |
- | main_proc.join(); | + | { |
- | } | + | counter = max; |
+ | } | ||
+ | |||
+ | public: | ||
+ | ... | ||
+ | BackwardCounter& operator --() | ||
+ | { | ||
+ | if (counter > 0) | ||
+ | counter = counter - 1; | ||
+ | else | ||
+ | reachMinimum(); | ||
+ | return *this; | ||
+ | } | ||
+ | BackwardCounter operator --(int) | ||
+ | { | ||
+ | BackwardCounter result(*this); | ||
+ | operator--(); | ||
+ | return result; | ||
+ | } | ||
+ | ... | ||
+ | }; | ||
</code> | </code> | ||
- | </hidden> | + | Attention pour ''BiDiCounter'', il faut redéfinir les opérateurs, puisque le type des opérateurs est lié au type de la classe. Donc nous devons ajouter à ''BiDiCounter'' le code suivant : |
+ | <code cpp> | ||
+ | class BiDiCounter : public ForwardCounter, public BackwardCounter | ||
+ | { | ||
+ | ... | ||
+ | public: | ||
+ | ... | ||
+ | BiDiCounter& operator ++() | ||
+ | { | ||
+ | ForwardCounter::operator++(); | ||
+ | return *this; | ||
+ | } | ||
+ | BiDiCounter operator ++(int) | ||
+ | { | ||
+ | BiDiCounter result(*this); | ||
+ | ForwardCounter::operator++(); | ||
+ | return result; | ||
+ | } | ||
+ | BiDiCounter& operator --() | ||
+ | { | ||
+ | BackwardCounter::operator--(); | ||
+ | return *this; | ||
+ | } | ||
+ | BiDiCounter operator --(int) | ||
+ | { | ||
+ | BiDiCounter result(*this); | ||
+ | BackwardCounter::operator--(); | ||
+ | return result; | ||
+ | } | ||
+ | ... | ||
+ | }; | ||
+ | </code> | ||
+ | |||
+ | </hidden> | ||