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 :

Problème de conception : Héritage et template


Sujet :

C++

  1. #1
    Membre régulier
    Inscrit en
    Septembre 2010
    Messages
    73
    Détails du profil
    Informations forums :
    Inscription : Septembre 2010
    Messages : 73
    Points : 90
    Points
    90
    Par défaut Problème de conception : Héritage et template
    Bonjour,

    Je suis actuellement géné par un problème d'héritage.

    J'ai une classe template "Hierarchy" que je voudrais faire hériter à certains objets. Cela permettrai à un objet qui en hérite d'avoir la possibilité d'ajout de fils.
    Je vais essayer d'illustrer un peu mes propos :
    J'ai une classe Hierarchy comme suis.

    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
    template <typename T>
    class Hierarchy
    {
    public:
     
        Hierarchy ();
        ~Hierarchy(){};
     
        void setParent(T * parent){ ... }; // set le pere
        T * parent(){ return _parent;};    // retourne le pere
     
        void addChild(T * child){ ...};    // ajoute un fils
        std::list<T*> & children() {...};      // retourne la liste de fils
     
    protected:
        std::list <T*> _children;
        T * _parent;
    };

    Et voudrais qu'un objet, par exemple "Item", qui en hérite puisse avoir une liste de fils et la possibilité d'avoir un père.
    Le tout en faisant un simple héritage, sans surcharger ou réimplémenter aucune fonction, du style :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    class Item : public Hierarchy
    {
    public:
     
        Item () : Hierarchy();
        ~Item(){};
    };
    En pratique ça donnerai :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    Item * pere = new Item();
    Item * fils1 = new Item();
    Item * fils2 = new Item();
     
    pere->addChild(fils1);
    pere->addChild(fils2);
     
    for (int i = 0;  pere->children().size; i++)
    {
         Item * currentChild = pere->children().at(i); //Sans devoir faire de cast
    }

    Avec la méthode que j'ai fait je suis obligé de caster un objet de type T en un objet Item avant de m'en servir, ou encore réimplémenter les fonctions dans la classe Item. Je voudrais éviter cette étape.

    Merci à ceux qui pourront m'éclairer sur la façon dont il faut faire.

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

    Informations professionnelles :
    Activité : aucun

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

    Cela compile, avec le code que tu donnes

    En toute logique, tu devrais déjà avoir un refus du compilateur au niveau de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    class Item : public Hierarchy
    sous prétexte que le nombre d'arguments template ne correspond pas (aucun passé alors qu'un est attendu)

    De même, il manque les accolades et la précision du type devant spécialiser Hierarchy pour le constructeur de Item

    Si tu corriges déjà ce seul point en utilisant le CRTP, tout rentrera dans l'ordre
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    class Item : public Hierarchy<Item>
    {
        public:
            /* NOTA: pas obligatoire étant donné qu'il y a un constrcteur
             * par défaut pour Hierarchy
             */
            Item():Hierarchy<Item>(){}
            /* virual */ ~Item(){}
    };
    NOTA: N'oublie pas que tout objet qui dérive de Item pourra effectivement être utilisé, mais que si tu crées une autre classe (Item2 ) qui hérite de Hierarchy<Item2>, Item et Item2 ne pourront pas cohabiter au sens polymorphe du terme
    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 régulier
    Inscrit en
    Septembre 2010
    Messages
    73
    Détails du profil
    Informations forums :
    Inscription : Septembre 2010
    Messages : 73
    Points : 90
    Points
    90
    Par défaut
    Merci beaucoup !

    J'ai codé les classes Hierarchy et Item et tout compile et fonctionne.

    Vos indications m'ont permis de voir où est vraiment mon erreur. En fait ma classe Hierarchy devait déjà fonctionner. Je pensais que mon problème de cast venait de la manière de l'implémenter.

    J'ai donc découvert que mon problème se situait ailleurs :

    - j'ai un "SousItem" qui dérive d'Item. L'ennuie c'est que l'appelle de la fonction parent() me renvoie un objet de type Item, ce qui est normal vu la manière dont les classes sont faites.

    Alors ma question est : comment faire pour que chaque objet héritant d'Item puisse me retourner leur type et non celui de la classé hérité ?

    Illustration :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    class SousItem1 : public Item
    {
        public:
            SousItem1():Item(){};
            ~SousItem1(){};
    };
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    SousItem1 * sousItem1_Parent = new SousItem1();
    SousItem1 * sousItem1_Child = new SousItem1();
     
    sousItem1_Parent->addChild(sousItem1_Child);
     
    // L'instruction ci-dessous ne fonctionne pas:
    // sousItem1_Child->parent() renvoie un Item
    SousItem 1 * sousItem_Temp = sousItem1_Child->parent()
    A l'heure actuelle sousItem1_Child->parent() me retourne un Item, alors que je voudrais que cela soit un SousItem1.
    Est-t-il possible de faire ça sans caster quoique ce soit ?

    Merci.

    En attendant je vais refaire un tour sur la FAQ de template au cas où j'aurais un déclic...

  4. #4
    Expert éminent sénior

    Avatar de dragonjoker59
    Homme Profil pro
    Software Developer
    Inscrit en
    Juin 2005
    Messages
    2 045
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Bas Rhin (Alsace)

    Informations professionnelles :
    Activité : Software Developer
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2005
    Messages : 2 045
    Points : 11 368
    Points
    11 368
    Billets dans le blog
    10
    Par défaut
    Je ne vois que 2 façons de faire ça (mais je peux me tromper) :
    - SousItem1 implémente la fonction parent()
    - SousItem1 n'étend pas Item mais Hierarchy<SousItem1> (si c'est bien elle qui implémente la fonction parent() )
    Si vous ne trouvez plus rien, cherchez autre chose...

    Vous trouverez ici des tutoriels OpenGL moderne.
    Mon moteur 3D: Castor 3D, presque utilisable (venez participer, il y a de la place)!
    Un projet qui ne sert à rien, mais qu'il est joli (des fois) : ProceduralGenerator (Génération procédurale d'images, et post-processing).

  5. #5
    Expert éminent sénior
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Février 2005
    Messages
    5 069
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Conseil

    Informations forums :
    Inscription : Février 2005
    Messages : 5 069
    Points : 12 113
    Points
    12 113
    Par défaut
    Rendre "parent" virtuel et utiliser le covariance.

  6. #6
    Membre éclairé Avatar de MatRem
    Profil pro
    Inscrit en
    Décembre 2002
    Messages
    750
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2002
    Messages : 750
    Points : 693
    Points
    693
    Par défaut
    Tu peux aussi encore utiliser le concept de CRTP (si ta hiérachie n'est pas encore plus profonde) :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    template< typename T >
    class Item : public Hierarchy<T>
    ...
     
    class SousItem : public Item<SousItem>
    ...
    Tout dépend de ton cas d'utilisation (entre

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 612
    Points : 30 612
    Points
    30 612
    Par défaut
    La question que je me poserais en priorité, c'est
    es tu sur que la fonction parent d'un SousItem1 renverra effectivement un SouItem1
    Ce sera peut être effectivement le cas, mais, comme la fonction parent est définie (grâce au fait que Item hérite de Hierarchie<Item>) pour le type Item, tu dois t'attendre à ce que l'objet renvoyé par la fonction parent soit... de n'importe quel type dérivé de Item

    Si tu veux avoir la certitude que la fonction parent de SousItem1 renvoie effectivement un SousItem1 et non un autre type dérivé de Item, pourquoi faire hériter SousItem1 de Item et non de... Hierarchy<SousItem1>

    N'oublie pas que l'héritage est la relation la plus forte qui puisse exister entre deux objet dans le sens où elle représente une relation "EST-UN" à prendre dans toute la sémantique du terme.

    Elle implique que partout où tu attend un objet du type de base, tu peux passer un objet du type dérivé, mais également que, à partir du moment où tu récupère un objet du type de base, tu n'aie aucune certitude a priori quant au fait qu'il s'agisse d'un type dérivé donné
    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

  8. #8
    Membre régulier
    Inscrit en
    Septembre 2010
    Messages
    73
    Détails du profil
    Informations forums :
    Inscription : Septembre 2010
    Messages : 73
    Points : 90
    Points
    90
    Par défaut
    Merci à tous, j'ai réglé mon problème en prennant la méthode CRTP.

    Mon idée c'était d'avoir une classe Item qui hérite de Hierarchy afin d'y trouver des fonctions propres à chacun des objets :

    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 Item : public Hierarchy <Item>
    {
    public:
        Item();
     
        //Fonction d'Item
        void fonctionItemA();
        void fonctionItemB();
     
        //Fonction de hierarchy
        void setParent(T * parent){ ... };   // set le pere
        T * parent(){ return _parent;};      // retourne le pere
     
        void addChild(T * child){ ...};        // ajoute un fils
        std::list<T*> & children() {...};      // retourne la liste de fils
        //...
    };
    Mais je voulais que les SousItem1 ai toujours ces fonctions + ses propres fonctions 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
    class SousItem1: public Item
    {
    public:
        SousItem1();
    
        //Fonction de SousItem1
        void fonctionSousItem1A();
        void fonctionSousItem1B();
    
        //Fonction d'Item
        void fonctionItemA();
        void fonctionItemB();
    
        //Fonction de hierarchy
        void setParent(T * parent){ ... };   // set le pere
        T * parent(){ return _parent;};      // retourne le pere
     
        void addChild(T * child){ ...};        // ajoute un fils
        std::list<T*> & children() {...};      // retourne la liste de fils
        //...
    };
    Du coup j'ai réglé mon problème en faisant une classe intermédiaire Item0 dont hériterait Item.

    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
    class Item0:
    {
    public:
        //Fonction d'Item
        void fonctionItemA();
        void fonctionItemB();
     
    };
     
    template <typename T>
    class Hierarchy: 
    {
    public:
        //Fonction de hierarchy
        void setParent(T * parent){ ... };   // set le pere
        T * parent(){ return _parent;};      // retourne le pere
     
        void addChild(T * child){ ...};        // ajoute un fils
        std::list<T*> & children() {...};      // retourne la liste de fils
        //...
    };
     
    template <class T>
    class Item: public Item0, public Hierarchy<T>
    {
    public:
          Item : Item0(), public Hierarchy<T>(){}
     
    };
     
    class SousItem1: public Item<SousItem1>
    {
    public:
          SousItem1: Item<SousItem1>(){}
    };
    Pour faire simple Item0 est un objet précis.
    Item est le même objet qui peut être parenté avec ses semblable grâce à Hierarchy.
    Et SousItem1 hérite simplement d'Item et je peux y ajouté les fonctions que je veux. Tout ça sans la moindre redéfinition de fonction ou de cast.

    C'est parfait, encore merci à tous !

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Problème de conception avec héritage
    Par oodini dans le forum C++
    Réponses: 3
    Dernier message: 24/01/2013, 11h43
  2. Réponses: 10
    Dernier message: 08/06/2012, 09h22
  3. Réponses: 11
    Dernier message: 26/11/2011, 23h15
  4. Réponses: 0
    Dernier message: 09/11/2008, 14h33
  5. Réponses: 16
    Dernier message: 17/03/2007, 17h31

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