IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Langage C++ Discussion :

Récupérer un pointeur sur une classe à sa création


Sujet :

Langage C++

  1. #1
    Membre éprouvé
    Avatar de ChPr
    Homme Profil pro
    Inscrit en
    Septembre 2005
    Messages
    2 022
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 78
    Localisation : France, Val d'Oise (Île de France)

    Informations forums :
    Inscription : Septembre 2005
    Messages : 2 022
    Points : 1 049
    Points
    1 049
    Par défaut Récupérer un pointeur sur une classe à sa création
    Bonsoir à toutes et à tous,

    Suite au fil "Accès indirect aux éléments d'une classe" je souhaite récupérer un pointeur sur une classe à sa création.

    L'idée est d'utiliser "this".

    J'ai trouvé une première manière, mais qui ne me satisfait pas, car je récupère le pointeur justa après la création en appelant un fonction. Cette manière montre que, a priori, on peut utiliser "this". Voici ce que je fais :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    class Truc {
    public:
      int xx;
      Truc *getPtr() {
      return this;
      }
    };
     
    Truc *ptrs[10]; // Tableau de pointeur
     
    void setup() {
     
      ptrs[3] = truc.getPtr();
     
      truc.xx = 421;
      int b = (*ptrs[3]).xx; // Cela me fournit bien la valeur 421
    }
    Maintenant, je voudrais utiliser ce 'this" à la création du composant, j'écris :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    class Truc {
    class Truc {
    public:
      int xx;
      Truc(Truc *myPtr) { // passage de l'adresse du paramètre
        myPtr = this; // // je voudrais que le contenu du paramètre soit égal à "this" c'est-à-dire l'adresse de "truc"
      }
    };
     
    Truc *ptrs[10]; // tableau de pointeur de type "Truc"
     
    void setup() {
     
      Truc truc(ptrs[0]); // instanciation de Truc avec le premier élément du tableau en paramètre pour récupérer le pointeur "this"
     
      truc.xx = 421;
      int b = (*ptrs[0]).xx; // je récupère une valeur, mais ce n'est pas 421 !
    }
    J'ai essayé diverses façons d'écrire cela, mais, soit ça plante à la compilation, soit ça plante à l'exécution, soit encore je n’obtiens pas la bonne valeur.

    Est-ce la syntaxe qui n'est pas bonne ?

    Est-ce que la valeur de this n'est pas encore déterminée au moment où je la demande ?

    Merci de votre aide.

    Pierre

  2. #2
    Rédacteur/Modérateur
    Avatar de JolyLoic
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2004
    Messages
    5 463
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 5 463
    Points : 16 213
    Points
    16 213
    Par défaut
    Dans ton code, tu passes le pointeur par copie. Donc toutes les modifications que tu lui appliques à l'intérieur de la fonction ne sont pas visibles à l'extérieur.
    Si tu faisais :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
      Truc(Truc *&myPtr) { // passage de l'adresse du paramètre
        myPtr = this; // // je voudrais que le contenu du paramètre soit égal à "this" c'est-à-dire l'adresse de "truc"
      }
    ça devrait aller mieux.

    Maintenant, de là à dire que le code est beau... Il faudrait en savoir plus ce que tu veux faire à la base.
    Ma session aux Microsoft TechDays 2013 : Développer en natif avec C++11.
    Celle des Microsoft TechDays 2014 : Bonnes pratiques pour apprivoiser le C++11 avec Visual C++
    Et celle des Microsoft TechDays 2015 : Visual C++ 2015 : voyage à la découverte d'un nouveau monde
    Je donne des formations au C++ en entreprise, n'hésitez pas à me contacter.

  3. #3
    Membre éprouvé
    Avatar de ChPr
    Homme Profil pro
    Inscrit en
    Septembre 2005
    Messages
    2 022
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 78
    Localisation : France, Val d'Oise (Île de France)

    Informations forums :
    Inscription : Septembre 2005
    Messages : 2 022
    Points : 1 049
    Points
    1 049
    Par défaut
    Merci JolyLoic,

    Modifiant mon code selon ta proposition, cela fonctionne.

    Mais, étant un débutant en C++, je m'y perd entre les "*" et les "&".

    Tu dis : "Dans ton code, tu passes le pointeur par copie". Peux-tu m'expliquer pourquoi ?

    Tu écris : Truc(Truc *&myPtr) . Peux-tu me dire comment je dois lire *&myPtr

    Ce que je veux faire : Je développe sur Arduino : un jeu de cartes basé sur un µP et muni d'un module d'affichage avec la fonction écran tactile. Je voudrais faire un mini, que dis-je, un micro OS. Je m'explique : je crée des composants visuels (boutons, afficheurs, ...) et je voudrais que lorsque qu'on appuie dessus cela déclenche un évènement mais sans que j'ai à écrire, pour chaque composant, une fonction de test pour voir si l'appui le concerne.

    D'où l'idée d'inscrire dans un tableau, de manière automatique, chaque composant à sa création (l'indice du tableau étant automatiquement incrémenté à chaque création) et ensuite, grâce à une boucle infinie, parcourir le tableau pour vérifier si tel ou tel composant a reçu un appui. Ensuite, attacher une fonction à un composant si besoin est.

    NOTA 1 : il y a très longtemps, j'avais fais un tel OS sous DOS en turbo-pascal pour un logiciel de simulation d'automatisme. Un pré-windows en sorte avec menus déroulants et tutti quanti .

    NOTA 2 : la syntaxe du C m'a toujours effrayé .

    Cordialement.

    Pierre

  4. #4
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 614
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Salut,

    Ou bien, tu vas chercher beaucoup trop loin, ou bien, tu devrais envisager des solutions toutes autres.

    Il n'y a en effet aucune raison de demander à l'objet lui-même de renvoyer un pointeur sur lui-même, étant donné qu'il est toujours possible... d'en prendre l'adresse.

    Pour autant que truc soit une variable globale (ce que je te déconseille cependant) un code aussi simple que
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    class Truc {
    public:
      int xx;
      /* pas besoin de ca
      Truc *getPtr() {
      return this;
      }
      */
    };
     
    Truc *ptrs[10]; // Tableau de pointeur
     
    void setup() {
     
      ptrs[3] = &truc;
     
      truc.xx = 421;
      int b = (*ptrs[3]).xx; // Cela me fournit bien la valeur 421
    }
    fera parfaitement l'affaire

    Autrement, tu peux envisager l'allocation dynamique de la mémoire, soit sous la forme de ptrs[x]= new Truc; soit en envisageant, par exemple, la mise au point d'un système proche de la fabrique( essentiellement utile si la classe Truc est susceptible de servir de classe de base dans une hiérarchie de classes).

    Il faudra, cependant, penser à libérer correctement chaque objet créé de la sorte avec un delete correspondant, ou peut être (c'est plus sécurisant ) envisager l'utilisation de pointeurs intelligents, comme les std::unique_ptr ou les std::shared_ptr (si tu dispose de C++11)
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  5. #5
    Membre éprouvé
    Avatar de ChPr
    Homme Profil pro
    Inscrit en
    Septembre 2005
    Messages
    2 022
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 78
    Localisation : France, Val d'Oise (Île de France)

    Informations forums :
    Inscription : Septembre 2005
    Messages : 2 022
    Points : 1 049
    Points
    1 049
    Par défaut
    @ koala01 : dans le code que tu me proposes, je pense que tu as oublié d'instancier truc. le code devient alors :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    Truc truc;
    ptrs[3] = &truc;
    C'est ce que je voudrais éviter : avoir plusieurs lignes nécessaires à la création/enregistrement des composants créés.

    Voire même éviter d'avoir à fournir le paramètre ptrs dans l'instanciation, faire en sorte que cela se fasse dans une méthode du composant, mais alors, myPtr étant de type Truc, il est nécessaire de le déclarer avant la classe et donc il me faut une déclaration forward de Truc (ce que je ne sais pas encore faire, mais ça va venir ), par exemple :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
     
    int indexComp = 0;
     
    // Déclaration forward de Truc à faire
    Truc *ptrs[10]; // tableau de pointeur de type "Truc"
     
    class Truc {
    public:
      int xx;
      Truc() {
        myPtr[indexComp] = this;
        indexComp++;
      }
    };
     
    // Déclaration de composants :
     
    Truc truc;
    Truc machin;
    ...
    Tu écris "soit en envisageant, par exemple, la mise au point d'un système proche de la fabrique( essentiellement utile si la classe Truc est susceptible de servir de classe de base dans une hiérarchie de classes)."

    Truc étant très certainement le parent de tous mes composants, il va falloir que je me penche sur ce qu'est un "système de fabrique".

    NOTA : avec Arduino, je n'ai pas d'accès à tout ce qui est std::

    Cordialement.

    Pierre

  6. #6
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 614
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Citation Envoyé par ChPr Voir le message
    @ koala01 : dans le code que tu me proposes, je pense que tu as oublié d'instancier truc. le code devient alors :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    Truc truc;
    ptrs[3] = &truc;
    Je penses plutôt que tu as "zappé" la phrase qui précédait le code en quetion, que je cite ici:
    Pour autant que truc soit une variable globale (ce que je te déconseille cependant) u
    C'est ce que je voudrais éviter : avoir plusieurs lignes nécessaires à la création/enregistrement des composants créés.
    a priori, ta solution passe donc par l'allocation dynamique de la mémoire
    Voire même éviter d'avoir à fournir le paramètre ptrs dans l'instanciation, faire en sorte que cela se fasse dans une méthode du composant,
    Mais la question est alors : pourquoi voudrais tu que cela passe par une fonction membre de l'objet

    Cela donnerait à l'objet une responsabilité qu'il n'a absolument pas à avoir

    Tu écris "soit en envisageant, par exemple, la mise au point d'un système proche de la fabrique( essentiellement utile si la classe Truc est susceptible de servir de classe de base dans une hiérarchie de classes)."

    Truc étant très certainement le parent de tous mes composants, il va falloir que je me penche sur ce qu'est un "système de fabrique".
    L'idée est de faire en sorte que tu n'aies pas à connaître le type réel d'un objet afin de pouvoir l'utiliser.

    Quand on y réfléchit, siTruc sert des base à une hiérarchie de classe, chaque objet sait pertinemment quel est son type réel, ce qui fait que le seul moment où tu as besoin "qu'autre chose" connaisse le type réel d'un objet est... le moment de sa création.

    Le patron de conception "fabrique" (desing pattern Factory, en anglais) consiste à déléguer la création des différents objets réel à quelque chose qui ne fera que cela (la fabrique proprement dite) et qui renverra d'office l'objet (créé à coup de new) sous la forme d'un pointeur vers un objet de la classe de base.

    De cette manière, tu n'as même plus à t'inquiéter des différents types dérivés de ta classe de base : tout ce que tu dois connaitre, c'est la classe de base elle-même et son interface publique

    Enfin, je prend un raccourci honteux à ce niveau, car, il arrive que le polymorphisme ne suffise simplement pas et qu'il soit malgré tout nécessaire de pouvoir connaître le type réel d'un objet dans certaines circonstances bien particulières.

    Dans ce cas, on peut alors envisager le recours au double dispatch, dont l'une des mise en oeuvre est le très connu patron de conception visiteur (desing pattern visitor en anglais )
    NOTA : avec Arduino, je n'ai pas d'accès à tout ce qui est std::

    Cordialement.

    Pierre
    C'est bien dommage

    Et c'est une raison de plus pour envisager l'allocation dynamique de la mémoire, mais il est préférable de s'y prendre correctement.

    Tu devrais peut être envisager d'encapsuler ton tableau d'éléments (à moins que tu sois sur de n'avoir jamais besoin de plus de 10 objets) dans une classe dont la seule responsabilité sera de gérer la mémoire allouée dynamiquement à ce tableau (j'ai régulièrement parlé de la forme canonique de Coplien dans mes interventions, avec exemples et explications, une petite recherche sur le forum devrait te donner des pistes intéressantes ) et faire en sorte que ce soit cette classe qui s'occupe de libérer correctement la mémoire allouée aux différentes pointeurs

    Ceci dit, Truc prend alors ce que l'on appelle une sémantique d'entité, ce qui en ferait une classe non copiable et non assignable (de même que pour les classes dérivées de Truc) ce qui implique que la classe qui gérerait ton tableau de pointeurs aurait largement intérêt à ne pas pouvoir être copiée ni assignée non plus
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  7. #7
    Membre éprouvé
    Avatar de ChPr
    Homme Profil pro
    Inscrit en
    Septembre 2005
    Messages
    2 022
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 78
    Localisation : France, Val d'Oise (Île de France)

    Informations forums :
    Inscription : Septembre 2005
    Messages : 2 022
    Points : 1 049
    Points
    1 049
    Par défaut
    Citation Envoyé par koala01 Voir le message
    ... a priori, ta solution passe donc par l'allocation dynamique de la mémoire ...
    Les composants que je veux créer, sont des boutons, des afficheurs ... C'est du statique à 100 %. Par ailleurs la taille du tableau de composants ne sera pas fixe, il aura celle du nombre de composants (liste chainée)

    Citation Envoyé par koala01 Voir le message
    ... Mais la question est alors : pourquoi voudrais-tu que cela passe par une fonction membre de l'objet

    Cela donnerait à l'objet une responsabilité qu'il n'a absolument pas à avoir ...
    Il est de la responsabilité de chacun des composants d'être connu s'il veut pouvoir être scruté et avoir la focalisation si besoin est.

    Dans le mini programme tel que je l'écris ici, il est composé de deux parties :
    une partie sous la responsabilité du développeur de composants et qui ne sera pas visible de l'utilisateur des composants
    une partie utilisateur des composants : voici ce que ça donne :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    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
     
    /* ----------------------------------------------*/
    // Partie construite par le développeur du micro OS, de composants
    /* ----------------------------------------------*/
     
    int indComp = 0;
    class Truc; // déclaration forward de Truc
    Truc *ptrs[10]; // tableau de pointeur de type "Truc"
     
    class Truc {
    public:
      int xx;
      Truc() {
        ptrs[indComp] = this; // enregistrement de l'adresse du composant 
        indComp++; // incrémentation du compteur de composants
      }
    };
     
    /* ----------------------------------------------*/
    // Partie réalisée par l'utilisateur de composants
    /* ----------------------------------------------*/
     
    void utilisation() {
     
      Truc truc; // instanciation d'un composant
      truc.xx = 421;
      int b = (*ptrs[0]).xx; // récupération d'une valeur affectée à xx
      (*ptrs[0]).xx = 12; // affectation d'une valeur à xx
     
      Truc machin; // instanciation d'un autre composant
      machin.xx = 4210;
      b = (*ptrs[1]).xx;
     (*ptrs[1]).xx = 120; // affectation d'une valeur à xx
     
     while(true) { // test cyclique sur les composants; par exemple est-ce qu'on a appuyé dessus
      (for int i = 0; i< nbComp; i++) {
     
        if ( (*ptrs[i]).estAppuye) {
        // faire quelque chose}
        }
      }
    }
    Cordialement.

    Pierre

  8. #8
    Rédacteur/Modérateur


    Homme Profil pro
    Network game programmer
    Inscrit en
    Juin 2010
    Messages
    7 115
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : Canada

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : Juin 2010
    Messages : 7 115
    Points : 32 967
    Points
    32 967
    Billets dans le blog
    4
    Par défaut
    Bonjour,

    en gros tu veux faire une factory ? Alors pourquoi ne pas faire une factory justement ?
    Pensez à consulter la FAQ ou les cours et tutoriels de la section C++.
    Un peu de programmation réseau ?
    Aucune aide via MP ne sera dispensée. Merci d'utiliser les forums prévus à cet effet.

  9. #9
    Membre éprouvé
    Avatar de ChPr
    Homme Profil pro
    Inscrit en
    Septembre 2005
    Messages
    2 022
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 78
    Localisation : France, Val d'Oise (Île de France)

    Informations forums :
    Inscription : Septembre 2005
    Messages : 2 022
    Points : 1 049
    Points
    1 049
    Par défaut
    Citation Envoyé par Bousk Voir le message
    Bonjour,

    en gros tu veux faire une factory ? Alors pourquoi ne pas faire une factory justement ?
    Ça doit y ressembler fortement, mais je ne sais pas ce que c'est, il faut que je me renseigne.

    Cordialement.

    Pierre

  10. #10
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 614
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Citation Envoyé par ChPr Voir le message
    Les composants que je veux créer, sont des boutons, des afficheurs ... C'est du statique à 100 %. Par ailleurs la taille du tableau de composants ne sera pas fixe, il aura celle du nombre de composants (liste chainée)
    C'est bien la preuve que ce ne sera pas statique à 100%

    Tu devras, de toutes manières, choisir de créer un bouton OU un afficheur OU autre chose, et tu peux décider d'en créer "autant que tu veux".

    Il n'y a que du dynamisme dans cette optique
    Il est de la responsabilité de chacun des composants d'être connu s'il veut pouvoir être scruté et avoir la focalisation si besoin est.
    Justement, non :

    La responsabilité des différents composants, c'est de réagir en fonction des circonstances auxquels ils sont soumis.

    La responsabilité qui consiste à faire en sorte que les différents composants créés puisse travailler de concert échoit à quelqu'un d'autre :

    Tu dois avoir un "système" (une factory en l'occurrence) qui s'assure que ce sera bien l'objet que tu désires sera créé d'une part et qui peut (cela semble tout à fait dans ses cordes ) veiller à ce que l'objet créé soit "enregistré" auprès d'un "autre système" qui veillera à les faire travailler de concert (en gros, à les scruter régulièrement et / ou à leur donner le focus en cas de besoin, pour reprendre tes propres termes )

    Ça doit y ressembler fortement, mais je ne sais pas ce que c'est, il faut que je me renseigne.
    Allez, un petit coup de wikipedia pour t'aider.

    Je n'aime pas vraiment l'exemple qu'il donne, je te propose donc un autre lien sans doute plus en relation avec ce que tu veux (en anglais, désolé )
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  11. #11
    Membre éprouvé
    Avatar de ChPr
    Homme Profil pro
    Inscrit en
    Septembre 2005
    Messages
    2 022
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 78
    Localisation : France, Val d'Oise (Île de France)

    Informations forums :
    Inscription : Septembre 2005
    Messages : 2 022
    Points : 1 049
    Points
    1 049
    Par défaut
    Citation Envoyé par koala01 Voir le message
    ... La responsabilité des différents composants, c'est de réagir en fonction des circonstances auxquels ils sont soumis.

    La responsabilité qui consiste à faire en sorte que les différents composants créés puisse travailler de concert échoit à quelqu'un d'autre :

    Tu dois avoir un "système" (une factory en l'occurrence) qui s'assure que ce sera bien l'objet que tu désires sera créé d'une part et qui peut (cela semble tout à fait dans ses cordes ) veiller à ce que l'objet créé soit "enregistré" auprès d'un "autre système" qui veillera à les faire travailler de concert (en gros, à les scruter régulièrement et / ou à leur donner le focus en cas de besoin, pour reprendre tes propres termes ) ...
    Tu as raison, le fait que j'attribuais cette fonction au composant de base était une vue minimaliste du "système".

    J'ai vu ce qu'était une fabrique. C'est effectivement l'esprit de ce que je souhaite faire. Pour autant, je ne pense pas que cela résolve mon problème de pointeur polymorphe (tableau de pointeurs sur des descendants différents du composant de base, donc de types différents)

    Je pourrais me contenter d'un pointeur du type du composant de base pour savoir si on a appuyé dessus, mais si je veux attacher une fonction spécifique à certains composants, il me semble qu'il faut un pointeur sur ce type de descendant (à vérifier).

    Par ailleurs, sous Arduino (c'est un environnement assez limité ), les static_cast et dynamic_cast ont l'air d'avoir des fonctions limitées (compilateur ayant l'option rtti otée).

    Cordialement.

    Pierre

  12. #12
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 614
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Citation Envoyé par ChPr Voir le message
    Je pourrais me contenter d'un pointeur du type du composant de base pour savoir si on a appuyé dessus, mais si je veux attacher une fonction spécifique à certains composants, il me semble qu'il faut un pointeur sur ce type de descendant (à vérifier).
    C'est là qu'interviennent les notions de polymorphisme et de double dispatch.

    Le polymorphisme va permettre d'adapter le (les) comportement(s) d'un objet spécifique en fonction du type réel de l'objet au départ duquel le comportement est invoqué.

    Il est obtenu par utilisation de fonctions virtuelles : une seule et même fonction (même nom, même type de retour, même nombre et même type pour les paramètres) dont on peut spécifier un comportement pour chaque classe dérivée, par exemple:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    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
    class Base{
        public:
             // pour une classe de base, on ne peut pas déterminer le comportement 
             // en question
             // on la déclare donc comme fonction virtuelle pure (ce qui, de plus,
             // rend la classe abstraite, donc impossible à instancier en tant que telle )
            virtual void printMe() const = 0;
    }
     
    class Derivee1 ::Base{
        public:
            virtual void printMe() const {std::cout<<"je suis Derivee 1"<<std::endl;}
    }
    class Derivee2 ::Base{
        public:
            virtual void printMe() const {std::cout<<"je suis Derivee 2"<<std::endl;}
    }
    int main(){
        // Base * b = new Base; interdit : Base est une classe abstraite
        Base * d1 = new Derivee1;
        Base * d2 = new Derivee2;
        d1->printMe(); // ==> "je suis Derivee 1"
        d2->printMe(); // ==> "je suis Derivee 2"
        /* ... */
        delete d1;
        delete d2;
        return 0;
    }
    Quand ce genre de comportement devient insuffisant, on peut envisager le "double dispatch" : on se base sur le fait que chaque objet sait pertinemment de quel type réel il est pour lui demander de se transmettre à une fonction (qui, pour la cause, prendra une référence sur le type réel).

    Si, avec les mêmes classes que plus haut, nous prévoyons une fonction print avec une surcharge pour Derivee1 et Derivee2, sous la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    void print(Derivee1 const & d1){
        std::cout<<"j'ai recu une Derivee 1"<<std::endl;
    }
    void print(Derivee2 const & d2){
        std::cout<<"j'ai recu une Derivee 2"<<std::endl;
    }
    et que l'on modifie la fonction printMe() de Derivee1 et de Derivee2 sous la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    class Derivee1 ::Base{
        public:
            virtual void printMe() const {print(*this);}
    }
    class Derivee2 ::Base{
        public:
            virtual void printMe() const {print(*this);}
    }
    le code suivant fera bel et bien appel au double dispatch
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    int main(){
        // Base * b = new Base; interdit : Base est une classe abstraite
        Base * d1 = new Derivee1;
        Base * d2 = new Derivee2;
        d1->printMe(); // ==> "j'ai recu une Derivee 1"
        d2->printMe(); // ==> "j'ai recu une Derivee 2"
        /* ... */
        delete d1;
        delete d2;
        return 0;
    }
    (note : printMe pourrait d'ailleurs appeler une fonction différente pour Derivee1 et pour Derivee2).

    Il existe d'ailleurs le patron de conception visiteur qui présente une forme d'utilisation classique du double dispatch

    Voici un petit lien pour t'aider à le comprendre.
    Par ailleurs, sous Arduino (c'est un environnement assez limité ), les static_cast et dynamic_cast ont l'air d'avoir des fonctions limitées (compilateur ayant l'option rtti otée).
    Raison de plus pour utiliser une des deux techniques présentées dans cette intervention
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  13. #13
    Membre éprouvé
    Avatar de ChPr
    Homme Profil pro
    Inscrit en
    Septembre 2005
    Messages
    2 022
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 78
    Localisation : France, Val d'Oise (Île de France)

    Informations forums :
    Inscription : Septembre 2005
    Messages : 2 022
    Points : 1 049
    Points
    1 049
    Par défaut
    Je te remercie vraiment koala01 pour cette présentation.

    Cependant, si j'ai bien compris (mais peut-être que pas ) une fabrique s'applique pour des classes qui ont des comportements différents mais à partir de méthodes identiques (méthode identique "dessiner" mais dessins différents : un rond, un rectangle, ...).

    Or pour deux des classes que veux réaliser : "Boutons" et "Afficheurs" par exemple, leurs comportements sont vraiment différents. Par exemple :

    pour le bouton : appuyé/relâché ;mode bascule ou appui simple ...
    pour l'afficheur : position du texte, position du pointeur de caractère en cours ...

    Ce sont des méthodes différentes qui traitent cela.

    Bien sûr, je pourrais définir les mêmes méthodes avec les mêmes paramètres mais dont la signification serait totalement différente pour chaque type. Cela serait très certainement assez illisible, bien que fonctionnellement correct. oui, non ?

    Cordialement.

    Pierre

Discussions similaires

  1. Récupérer un pointeur sur une classe Singleton
    Par slake13 dans le forum Débuter
    Réponses: 7
    Dernier message: 18/11/2008, 17h01
  2. Réponses: 8
    Dernier message: 30/05/2006, 01h26
  3. [C++] pointeur sur une classe
    Par PH69 dans le forum Débuter
    Réponses: 1
    Dernier message: 21/11/2005, 22h08
  4. Réponses: 14
    Dernier message: 14/03/2005, 09h16
  5. [MFC] Problème pointeur sur une classe
    Par mick74 dans le forum MFC
    Réponses: 7
    Dernier message: 14/04/2004, 14h17

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo