L’utilisation d’un functor à la place d’un pointeur de fonction, comme nous venons de le voir, offre une très importante possibilité : il peut contenir des données-membres susceptibles d’être modifiées, et qui peuvent changer dynamiquement le comportement de l’opérateur ()
.
De même, si le functor est une donnée-résultat, certaines de ces données-membres peuvent être récupérées après l’appel de moulinette()
.
C’est ce que nous allons faire ici.
Travail à effectuer
Créer le projet FunctorInOut
.
Remplacer le contenu de votre fichier main.cpp
par le contenu du fichier TestFunctorAbstraitCorr.cpp
(corrigé de l’exercice précédent) :
/** * * @file TestFunctorAbstraitCorr.cpp * * @authors M. Laporte * * @date 07/05/2018 * * @version V1.0 * * @brief functor abstrait * **/ #include <string> #include <iostream> #include <cctype> // ispunct(), islower(), isalpha() // tolower(), toupper() #include <cassert> using namespace std; namespace { class ITraitCar { public : virtual ~ITraitCar (void) {} virtual int operator () (int caract) const = 0; }; // ITraitCar class ToLower : public ITraitCar { public : virtual ~ToLower (void) {} virtual int operator () (int caract) const noexcept { return tolower (caract); } // operateur() }; // ToLower class ToUpper : public ITraitCar { public : virtual ~ToUpper (void) {} virtual int operator () (int caract) const noexcept { return toupper (caract); } // operateur() }; // ToUpper class IgnPunct : public ITraitCar { public : virtual ~IgnPunct (void) {} virtual int operator () (int caract) const noexcept { return ispunct (caract) ? ' ' : caract; } // operateur() }; // IgnPunct string & moulinette (string & str, const ITraitCar & transf) { for (string::size_type i (str.size()); i--; ) str [i] = transf (str [i]); return str; } // moulinette() void testFunctor (void) { cout << "Functor abstrait : "; string ligne ("azert:;,.?GFDSQ"); string minusc ("azert:;,.?gfdsq"); string majusc ("AZERT:;,.?GFDSQ"); string sansPunct ("AZERT GFDSQ"); assert (minusc == moulinette (ligne, ToLower ())); assert (majusc == moulinette (ligne, ToUpper ())); assert (sansPunct == moulinette (ligne, IgnPunct())); cout << "OK\n"; } // testFunctor() } // namespace int main (void) { /* */ testFunctor (); /* */ return 0; } // main()
Dans la classe ITraitCar
, ajoutez :
- la donnée-membre
myCpt
, qui est un compteur modifiable par l’opérateur()
(on ne sait pas encore comment, puisque la fonction est abstraite !), - un constructeur qui initialise à
0
la donnée-membremyCpt
, - l’accesseur et le modifieur correspondants.
Dans la classe ToUpper
, modifiez l’opérateur ()
pour qu’il cumule le nombre de fois qu’il a été appelé.
Dans la classe ToLower
, modifiez l’opérateur ()
pour qu’il cumule le nombre de fois qu’il a été appelé sur une lettre (minuscule ou majuscule).
Dans la classe IgnPunct
, modifiez l’opérateur ()
pour qu’il cumule le nombre de caractères remplacés.
Dans la fonction testFunctor()
, vérifiez au moyen d’un assert()
le nombre de transformations effectuées après chaque appel à moulinette()
.
Remarque
la réalisation exacte de l’énoncé ci-dessus conduit à une erreur de compilation.
En effet, l’opérateur ()
modifie le contenu de la donnée-membre myCpt
, donc il ne devrait pas être suivi de const
.
De même, le functor passé en paramètre à la fonction moulinette()
est une donnée-résultat, puisqu’on en récupère le contenu après traitement.
Deux solutions peuvent être envisagées :
- transformer l’opérateur
()
en accesseur/modifieur (suppression deconst
de son profil) et transformer le paramètre formel functor demoulinette()
en donnée-résultat (supprimer son qualifieurconst
), - qualifier la donnée membre
myCpt
de la classe mère “mutable“, car l’opérateur()
est déclaréconst
et pourtant,
il modifie l’objet …
Cette solution correspond à l’idée que ces comptages sont faits “à l’insu” de l’utilisateur, par exemple dans une optique de mise au point ou de profiling de programme (étude du comportement d’un programme, statistiques d’utilisation, etc… en vue de son optimistion).
C’est cette dernière solution que nous présentons dans le corrigé.