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

 C++ Discussion :

Mémorisation d'un pointeur


Sujet :

C++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre confirmé
    Inscrit en
    Janvier 2006
    Messages
    173
    Détails du profil
    Informations forums :
    Inscription : Janvier 2006
    Messages : 173
    Par défaut Mémorisation d'un pointeur
    Bonjour,

    J'ai cinq classes A, B, C, D et Z.
    Dans ma classe Z qui est une classe contenant des symboles, les instances sont soient de type A, B, C ou D.
    Je souhaiterais savoir pour chaque instance de Z vers quelle instance de A, B, C ou D elle pointe.
    J'imagine donc un pointeur générique qui pourrait mémoriser un pointeur vers A ou B ou C ou D.
    La taille des pointeurs vers A, B, C, D n'est pas la même car les classes sont totalement différentes.
    On pourrait imaginer écrire dans le constructeur de Z :

    type_symbole = "type_A";
    pointeur_vers_classe = new A(...); // constructeur de A

    Mais je ne vois pas comment déclarer pointeur_vers_classe.
    Merci pour votre aide

  2. #2
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Salut,

    Le plus facile pour y arriver, c'est que A,B,C et D fassent partie d'une même hiérarchie de classe (par exemple, que B, C et D héritent toutes les trois de A).

    Il "suffit" alors d'utiliser un pointeur sur la classe de base, et tu pourra, la substitution aidant, y placer un pointeur sur n'importe quel objet dérivé de la classe de base.
    Cela pourrait ressembler à quelque chose comme
    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
    class A
    { 
        /* ...*/
    };
    class B : public A
    { 
        /* ...*/
    };
    class C : public A
    { 
        /* ...*/
    };
     
    class D : public A
    { 
        /* ...*/
    };
    class Z
    {
     
        private:
            A * ptr_;
    };
    Mais encore faut il que cela ait un sens !!!

    J'ai, en effet, du mal à imaginer que l'on puisse trouver un point commun "utile et justifié" entre un oiseau, une tronçonneuse, une voiture et une fleur


    Dans ce cas, il faut utiliser le principe du "type ereasure", et il est sans doute bon de te tourner vers boost::variant (si tes classes on sémantique d'entité) ou vers boost::any, si elles ont sémantique de valeur

    Comme tu envisages l'utilisation d'un pointeur, tu devrais plutôt te tourner vers boost::variant, mais, ceci dit, il est peut etre utile de réfléchir à la relation réelle qui existe entre Z et les autres classes.

    En effet, si la relation est "simplement" de se dire que Z "fait référence" à un objet qui peut exister de manière indépendante, le pointeur semble en effet être la meilleure solution, et le choix se tournera "naturellement" sur boost::variant .

    Par contre, si Z utilise "une variable" d'un des autres type, en fonction d'un contexte quelconque et que la dite variable ne peut exister sans qu'il y ait un Z pour s'en occuper, on peut parfaitement envisager le fait de ne pas travailler avec un pointeur, mais carrément avec un objet.

    Et, si cet objet a sémantique de valeur (qu'il est copiable, et affectable, et qu'il ne fait partie d'aucune hiérarchie de classe), on peut parfaitement envisager l'utilisation de boost::any

    Mais avant d'en arriver là, il y a malgré tout deux questions qu'il semble intéressant de se poser...

    La première est celle du but que tu poursuis en plaçant une référence parfois sur un objet de type A, parfois sur un objet de type B, ou C ou D.

    Est-ce que c'est pour que ta classe Z puisse les manipuler "en interne" (comprend : que l'utilisateur puisse tout ignorer de A, B, C et D quand il manipule Z, une fois qu'elle a été correctement "initialisée"), ou est-ce pour que Z puisse fournir un accès à l'objet référencé

    La deuxième question est de savoir s'il est vraiment "utile et opportun" de pouvoir disposer, en même temps de deux objets de type Z dont l'un référence un A et l'autre un B (ou un C ou un D)

    Si tu envisages de ne travailler, à un moment donné, uniquement avec des objets de type Z qui manipulent des A OU uniquement avec des objets de type Z qui ne manipulent que des B (ou C ou D, encore une fois ), un Z<A> devra surement fournir la même interface public qu'un Z<B> ou qu'un Z<C> (ou encore qu'un Z<D>) -- et bien sur, adapter le "comportement interne" au fait qu'il manipule un A, un B, un C ou un D -- mais il n'est pas forcément utile que les quatre solutions aient une base commune. On peut alors très bien envisager de travailler de manière générique avec, d'une part, un "helper" template, spécialisée pour travailler respectivement avec un A, un B, un C ou un D sous une forme proche de
    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
    template <typename Type>
    class MyHelper;
    template <>
    class MyHelper<A>
    {
        typeDeRetour operator()(A const & a)
        {
            /* traitement spécifique à A */
        }
    };
    template <>
    class MyHelper<B>
    {
        typeDeRetour operator()(B const & b)
        {
            /* traitement spécifique à B */
        }
    };
    template <>
    class MyHelper<C>
    {
        typeDeRetour operator()(C const & c)
        {
            /* traitement spécifique à C */
        }
    };
    template <>
    class MyHelper<D>
    {
        typeDeRetour operator()(D const & d)
        {
            /* traitement spécifique à D */
        }
    };
    et de créer une classe générique pour Z proche de
    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
    template <typename Type>
    class Z
    {
        public:
            /* note que cela fonctionne aussi parfaitement avec des références
             * et que l'idéal serait carrément de travailler avec des pointeurs
             * intelligents s'il faut vraiment que l'objet référencé soit un pointeur ;)
             */
            Z(Type * pointer):pointer_(pointer){}
            typeDeRetour doSomething() /* const */
            {
                 return MyHelper<Type>()(*pointer_);
            }
            /* et l'on peut même envisager de renvoyer l'objet référencé,
             * sous a forme d'une référence (constante ou non) ou d'un pointeur ;)
             */
            /* const */ Type & referedObject() /* const */
            {
                return *pointer_;
            }
        private:
            Type * pointer_;
    };
    Note d'ailleurs que si tu veux pouvoir manipuler en même temps des Z qui référencent des A et d'autres qui référencent des B, des C ou des D, cette classe peut, au pris de quelques aménagements, t'éviter de recopier trop souvent du code

    On garderait les "helpers" tels que je les ai décrit plus haut, et l'on utiliserait le pattern NVI sous une forme proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    class ZBase
    {
        public:
        /* ... */
            typeDeRetour something() /* const*/
            { return doSomething();}
        private:
            typeDeRetour doSomething() /* const */ = 0;
    };
    dont hériterait notre classe template Z sous une forme proche de
    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
    template <typename Type>
    class Z :public ZBase
    {
        public:
            /* note que cela fonctionne aussi parfaitement avec des références
             * et que l'idéal serait carrément de travailler avec des pointeurs
             * intelligents s'il faut vraiment que l'objet référencé soit un pointeur ;)
             */
            Z(Type * pointer):pointer_(pointer){}
            /* et l'on peut même envisager de renvoyer l'objet référencé,
             * sous a forme d'une référence (constante ou non) ou d'un pointeur ;)
             *
             * ...Après avoir pu déterminer si Z manipule un A, un B, un C ou un D
             */
            /* const */ Type & referedObject() /* const */
            {
                return *pointer_;
            }
        private:
     
            typeDeRetour doSomething() /* const */
            {
                 return MyHelper<Type>()(*pointer_);
            }
            Type * pointer_;
    };
    Voilà, je crois avoir exploré toutes les pistes possibles, ou, du moins, toutes celles qui me venaient à l'esprit.

    Je vais donc m'en arrêter là avant d'écrire un roman, mais n'hésites pas à demander plus d'information si tu ne comprends pas une chose ou l'autre
    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

  3. #3
    Membre confirmé
    Inscrit en
    Janvier 2006
    Messages
    173
    Détails du profil
    Informations forums :
    Inscription : Janvier 2006
    Messages : 173
    Par défaut
    Merci pour ta réponse.
    Je comprends dans la globalité.
    Ne serait il pas plus simple dans la classe Z d'avoir comme attributs :

    A* A_pointer;
    B* B_pointer;
    C* C_pointer;
    D* D_pointer;

    et de documenter le pointeur correspondent suivant le type d'objet A, B, C ou D. Les autres seraient dcumentés avec NULL ?

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    En terme de facilité, c'est discutable, car tu devrais alors prévoir quatre constructeur, ou les quatre mutateurs directement, avec la difficulté de t'assurer, si tu fournis les mutateurs, que les pointeurs sont tous remis à NULL au moment où tu change de type.

    De plus, si ta classe utilise elle-même les pointeurs en interne, tu seras obligé de tester les quatre pointeurs pour savoir lequel référence un objet.

    Par contre, en terme d'évolutivité, ce serait une catastrophe

    Si tu viens, dans quelques mois, à vouloir rajouter une classe E ou une classe F parce que tes besoins ont évolué, tu devras revoir l'intégralité du code de Z, avec le risque d'oublier quelque chose

    Avec la solution basée sur un helper, tu n'auras qu'à le spécialiser pour ta nouvelle classe et le tour sera joué (sans avoir à toucher au code existant).

    De plus, si tu viens à avoir plusieurs helpers exécutant des choses différentes (car, en gros, il y en aura un par comportement ), et que tu en oublies un, tu auras directement une erreur de compilation t'indiquant que tu as oublié de le définir
    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 confirmé
    Inscrit en
    Janvier 2006
    Messages
    173
    Détails du profil
    Informations forums :
    Inscription : Janvier 2006
    Messages : 173
    Par défaut
    Merci.
    Pourrions nous expliciter la première approche du helper qui me paraît plus simple.

    Pourquoi dans la classe template MyHelper fais tu une surcharge d'opérateur ? Qu'entends tu par "traitement spécifique à ..." ?

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Citation Envoyé par mulfycrowh Voir le message
    Pourquoi dans la classe template MyHelper fais tu une surcharge d'opérateur ?
    Pour avoir ce que l'on appelle un foncteur (ou un objet fonction):

    Une classe qui ne contient qu'une seule fonction que l'on peut appeler à l'aide des (), sous une forme proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    /* on crée la variable que le foncteur utilise */
    A myObject;
    /* on crée une variable du type foncteur */
    MyHelper<A> helper;
    helper(myObject);
    C'est une manière particulièrement efficace de travailler quand tu envisage des alogrithmes dont la seule différence est, finalement, la donnée sur laquelle le traitement est effectué.

    Par exemple, si tu envisage d'avoir un tableau de A, et que tu veux parfois le trier sur A.truc() et parfois sur A.machin(), tu peux très bien envisager d'avoir un foncteur qui comparera first.truc() à second.truc() et un autre qui comparera first.machin() à second.machin() afin de savoir, dans les deux cas, lequel est le plus petit.

    Le tri pourra alors se faire en une seule ligne grace à la fonction sort, disponible (comme tout ce qui est fourni par le standard) dans l'espace de nom std par simple inclusion du fichier d'en-tête <algorithm> sous la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    /* avec le foncteur LessByTruc sous la forme de
    struct LessByTruc
    {
        bool operator()(A const & first, A const & second) const
        {
            return first.truc() <second.truc();
        }
    };
    */
    std::sort(tab.begin(), tab.end(), LessByTruc());
    si tu veux le trier en fonction du résultat de a fonction truc() ou de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    /* avec le foncteur LessByTruc sous la forme de
    struct LessByMachin
    {
        bool operator()(A const & first, A const & second) const
        {
            return first.machin()<second.machin();
        }
    };
    */
    std::sort(tab.begin(), tab.end(), LessByMachin());
    si tu veux le trier en fonction du résultat de a fonction machin()
    Qu'entends tu par "traitement spécifique à ..." ?
    Tout simplement : tout ce que tu peux envisager de faire avec un objet de type A, B, C ou D en fonction du type de l'objet que tu manipules

    Si les classes A, B, C et D n'ont aucune relation d'héritage, il y a fort à parier (même si ce n'est pas forcément certain ) qu'elles présenteront des fonctions publiques différentes, et que tu les manipuleras donc de manière différentes

    Le "traitement spécifique à..." correspond à tout ce qui sera propre à ton objet de type A, B, C ou D et qui n'est pas forcément utilisable avec les autres types
    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

Discussions similaires

  1. Réponses: 2
    Dernier message: 20/04/2010, 11h05
  2. Techniques mémorisation pointeurs ?
    Par peter27x dans le forum Débuter
    Réponses: 26
    Dernier message: 13/02/2009, 15h01
  3. [Turbo Pascal] Allocation et désallocation de pointeurs dans une fonction
    Par neird dans le forum Turbo Pascal
    Réponses: 13
    Dernier message: 17/11/2002, 20h14
  4. djgpp et pointeurs far -2
    Par elvivo dans le forum Autres éditeurs
    Réponses: 16
    Dernier message: 29/07/2002, 22h43
  5. djgpp et pointeurs far
    Par elvivo dans le forum C
    Réponses: 2
    Dernier message: 13/07/2002, 00h44

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