1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
| /* D'un coté, nous avons une classe "Visiteur", qui peut être dérivé en différent types de visiteur "concret" et
* qui expose des comportements spécifiques pour chacune des classes dérivée
*
* Pour satisfaire le compilateur, il faut dresser la liste des classes susceptibles d'être manipulées ;-)
*/
class Base; // la classe de base
class Derivee; // une premiere classe dérivée
class AutreDerivee; // une deuxième classe dérivée
class NouvelleDerivee; // la classe dérivée que l'on ajoute plus tard (on ajoute cette ligne lors de l'ajout de la classe :D )
/* ... on peut en avoir tout plein, bien sur */
/* Cela nous permet de déclarer des fonctions virtuelles prenant une référence sur ces différents types, sous la forme de */
class Visiteur {
public:
/* dans un premier temps, on ne sait pas ce qu'il fera de ces données, on va donc les déclarer comme fonctions virutelles pures */
virtual void visit( Derivee /*const*/ & d) = 0;
virtual void visit(AutreDerivee /* const*/ & d) =0;
virtual void visit(NouvelleDerivee /* const*/ & d) =0;
/* on fera bien sur cela pour toutes les classes dérivées */
};
/* Et, d'un autre coté, nous aurons donc la hiérarchie de classes qui nous intéresse qui exposera un comportement
* permettant ... d'accepter le visiteur. C'est ce comportement qui sera défini pour chacune des classes dérivées, sous la forme
* de
*/
class Base{
public:
/* Les hierachies de classes ont, typiquement, sémantique d'entité. On ne veut en aucun cas pouvoir utiliser le constructeur de copie
* ou l'opérateur d'affectation
*/
Base(Base const &) = delete;
Base & operator= (Base const &) = delete;
/* C++11 est arrivé avec les notions d'affectation et de copie par déplacement, on ne veut pas d'avantage pouvoir y avoir recours */
Base(Base && ) = delete;
Base & operator=(Base && ) = delete;
/* on voudra par contre sans doute pouvoir détruire n'importe quelle instance d'une classe dérivée alors que nous
* la connaissons comme étant une instance de la classe de base
*/
virtual ~Base(); = default;
/* La magie du visiteur : le comportement accept, que nous devrons définir pour chacune des classes dérivées.
* Comme nous ne pouvons pas fournir de comportement adéquat pour la classe de base, nous en faisons une fonction
* virtuelle pure
*/
virtual void accept(Visiteur /* const */ & ) = 0;
};
/* dans les classes dérivées, nous implémentons le comportement accept, toujours de la même manière : en appelant
* le comportement visit du visiteur, et en lui transmettant la donée elle_même
*/
class Derivee : public Base{
public:
void accept(Visitor /* const*/ & v) override{
v.visit(*this);
}
/* Il peut y avoir d'autres trucs ici */
};
class AutreDerivee : public Base{
public:
void accept(Visitor /* const*/ & v) override{
v.visit(*this);
}
/* Il peut y avoir d'autres trucs ici */
};
class NouvelleDerivee : public Base{
public:
void accept(Visitor /* const*/ & v) override{
v.visit(*this);
}
/* Il peut y avoir d'autres trucs ici */
};
/* et bien sur, nous le ferons pour toutes les classes dérivées. */
/* Enfin, chaque fois que nous voudrons faire "quelque chose de spécial" avec nos données
* (qui sont, en réalité, des instances des classes dérivées, mais que nous connaissons comme étant
* des instances de la classe de base), nous pourrons créer un visiteur (concret, celui-i) pour lequel
* nous définissons le comportement visit, adapté à chacun des types des classes dérivées
*/
class VisiteurConcret : public Visiteur {
public:
/* dans un premier temps, on ne sait pas ce qu'il fera de ces données, on va donc les déclarer comme fonctions virutelles pures */
void visit( Derivee /*const*/ & d) override{
/* je sais manipuler une instance de la classe Derivee, que dois-je faire ici? */
}
void visit(AutreDerivee /* const*/ & d) override{
/* je sais manipuler une instance de la classe AutreDerivee, que dois-je faire ici? */
}
void visit(NouvelleDerivee /* const*/ & d) override{
/* je sais manipuler une instance de la classe NouvelleDerivee, que dois-je faire ici? */
}
/* on fera bien sur cela pour toutes les classes dérivées */
};
/* Tout cela nous permettra de travailler "facilement" dans la plupart des situations, par exemple, une fonction comme celle ci
*/
int main(){
std::vector<std::unique_ptr<Base>> datas; // C'est dans ce tableau que je maintiens l'ensemble des données
/* je remplis mon tableau d'une manière ou d'une autre */
/* puis vient le moment où je veux les utiliser
* je crée donc l'instance de mon visiteur concret */
VisiteurConcret visiteur;
/* et, pour chacune des données que je trouve dans le tableau, */
for(auto & ptr : datas){
/* je demande à la donnée d'accepter mon visiteur */
ptr->accept(visiteur);
}
} |
Partager