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 :

Héritage en diamant


Sujet :

Langage C++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre confirmé
    Profil pro
    Inscrit en
    Octobre 2008
    Messages
    124
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2008
    Messages : 124
    Par défaut Héritage en diamant
    Bonjour, je souhaite implémenter un héritage en diamant avec 3 classes de bases abstraites plus une classe normale. Le problème et que je n'arrive pas à mettre en place des attributs communs à toutes les classes.

    Pour être plus clair, j'ai imaginé un petit exemple :


    Voila mon code, qui ne compile pas :
    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
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    #ifndef _PERSONNAGE_H_
    #define _PERSONNAGE_H_
     
    class Personnage {
    public:
    	Personnage(string nom) : nom(nom) {}
    	virtual ~Personnage() {}
     
    	virtual string getNom() = 0;
    protected:
    	string nom;
    };
     
    #endif
     
     
    #ifndef _PERSOCOURAGEUX_H_
    #define _PERSOCOURAGEUX_H_
     
    class PersoCourageux : virtual public Personnage {
    public:
    	PersoCourageux(string nom, int ptsDeCourage) : Personnage(nom), ptsDeCourage(ptsDeCourage) {}
    	virtual ~PersoCourageux() {}
     
    	virtual string getNom() = 0;
    	virtual string getPtsDeCourage() { return ptsDeCourage; }
    private:
    	int ptsDeCourage;
    };
     
    #endif
     
     
    #ifndef _PERSOFORT_H_
    #define _PERSOFORT_H_
     
    class PersoFort : virtual public Personnage {
    public:
    	PersoFort (string nom, int ptsDeForce) : Personnage(nom), ptsDeForce(ptsDeForce) {}
    	virtual ~PersoFort () {}
     
    	virtual string getNom() = 0;
    	virtual string getPtsDeForce() { return ptsDeForce; }
    private:
    	int ptsDeForce;
    };
     
    #endif
     
     
     
    #ifndef _GUERRIER_H_
    #define _GUERRIER_H_
     
    class Guerrier : public PersoCourageux, public PersoFort {
    public:
    	Guerrier(string nom, int ptsDeCourage, int ptsDeForce) : ptsDeCourage(nom, ptsDeCourage), PersoFort (nom, ptsDeForce) {}
     
    	string getNom() { return this->nom; }
    };
     
    #endif
    Comment dois-je faire pour implémenter correctement ce type de structure ? Comment mettre en place des attributs communs aux classes filles ?

  2. #2
    Membre Expert
    Homme Profil pro
    Étudiant
    Inscrit en
    Juin 2012
    Messages
    1 711
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Juin 2012
    Messages : 1 711
    Par défaut
    Plusieurs erreurs de types, et tu peux implémenter getNom dans Personnage, pas besoin de mettre la méthode virtuelle pure pour avoir une class abstraite, suffit de mettre le constructeur protected.

    Tes classes PersoFort et PersoCourageux ne dérivent pas de Personnage. Il faut utiliser l'héritage virtuel.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    class PersoFort: public virtual Personnage { /*... */ };
    Tu devrais avoir un truc du genre
    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
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    #include <string>
    #include <iostream>
    using namespace std;
     
    class Personnage {
    public:
    	virtual ~Personnage() {}
    	virtual string getNom() const { return nom; };
     
    protected:
    	Personnage(const string& nom) : nom(nom) {}
     
    private:
    	string nom;
    };
     
    class PersoCourageux: public virtual Personnage {
    public:
    	virtual ~PersoCourageux() {}
    	virtual int getPtsDeCourage() const { return ptsDeCourage; }
     
    protected: 
    	PersoCourageux(const string& nom, int ptsDeCourage)
    		: Personnage(nom),
    		ptsDeCourage(ptsDeCourage) {}
     
    private:
    	int ptsDeCourage;
    };
     
     
    class PersoFort: public virtual Personnage {
    public:
    	virtual ~PersoFort () {}
    	virtual int getPtsDeForce() const { return ptsDeForce; }
     
    protected:
    	PersoFort (const string& nom, int ptsDeForce)
    		: Personnage(nom),
    		ptsDeForce(ptsDeForce) {}
     
    private:
    	int ptsDeForce;
    };
     
    class Guerrier: public PersoFort, public PersoCourageux{
    public:
    	Guerrier(const string& nom, int ptsDeCourage, int ptsDeForce)
    		: Personnage(nom), // résout l'ambiguité sur l'initialisation de Personnage
    		PersoCourageux(nom, ptsDeCourage),
    		PersoFort (nom, ptsDeForce) {}
     
    };
     
     
    int main(int argc, char **argv) {
    	Guerrier jacky("jacky", 42, 42);
    	cout << jacky.getNom() << " " << jacky.getPtsDeCourage() << " " << jacky.getPtsDeForce() << endl;
     
    	return 0;
    }
    Plus d'infos ici.
    http://loic-joly.developpez.com/arti...multiple/#L4-B

  3. #3
    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,

    L'apparition d'un héritage en diamant est, très régulièrement, le symptôme d'un problème de conception...

    C'est à telle enseigne que d'autres langages (C# et java, par exemple) interdisent l'héritage multiple, uniquement pour éviter cette situation (parce qu'ils font d'office hériter toute classe de Object)

    Ta question ne devrait donc pas être "comment pourrais-je implémenter un héritage en diamant pour créer mon guerrier" mais bien... "comment pourrais-je faire pour me passer de l'héritage en diamant"

    En se posant cette question, tu en arriverais sans doute à te dire que, finalement, n'importe quel personnage dispose d'une certaine dose de courage, d'une certaine force, d'une certaine agilité et sans doute d'un tas d'autres choses auxquelles je ne pense pas forcément

    L'idée n'est donc pas de se dire que "je vais faire hériter un type de personnage qui sera fort d'un coté, un autre qui sera courageux d'un autre, un troisième qui sera agile encore ailleurs, puis faire hériter mon guerrier du type courageux et du type fort", mais bien de se dire que je vais créer un type "guerrier" qui hérite (directement ) de personnage et qui, au moment de sa création, sera plus fort et plus courageux que les autres.

    Ensuite, bien sur, tu peux t'amuser car tu peux très bien demander (directement au personnage) de répondre (par oui ou par non) à des questions simples comme "es-tu courageux ?" ou "es-tu fort" ou encore "es-tu intelligent?".

    La réponse dépendant à ce moment là de la quantité de force, de courage ou d'intelligence dont le personnage dispose en fonction de son niveau, par exemple: quelqu'un ayant 10 d'intelligence au niveau 0 serait un véritable génie alors que quelqu'un n'ayant "que" 20 au niveau 15 n'aurait à peu de chose près que l'intelligence d'un navet
    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

  4. #4
    Membre confirmé
    Profil pro
    Inscrit en
    Octobre 2008
    Messages
    124
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2008
    Messages : 124
    Par défaut
    Citation Envoyé par Iradrille Voir le message
    Tes classes PersoFort et PersoCourageux ne dérivent pas de Personnage. Il faut utiliser l'héritage virtuel.
    J'ai oublié d'écrire les héritages en fait, mais ils étaient bien présent dans mon code de base.

    En tout cas, merci pour ta réponse Iradrille, j'ai enfin une méthode pour implémenter un héritage en diamant.

    Je vais essayer plusieurs façon de l'implémenter, sans constructeur en private avec une méthode virtuelle pure, avec une référence ou un pointeur vers la variable globale de la classe de base....

    Je ne sais pas si cela est possible ?

    koala01, j'ai bien conscience que la structure n'est pas obligatoire voire même non adaptée au problème, mais mon but avant tout est de savoir mettre en place un héritage en diamant.

  5. #5
    Rédacteur/Modérateur


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

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : Juin 2010
    Messages : 7 153
    Billets dans le blog
    4
    Par défaut
    Citation Envoyé par DavidleVrai Voir le message
    avec une référence ou un pointeur vers la variable globale de la classe de base....
    Pour cela, regarde le Design Pattern Composite (Composition).
    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.

  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
    En fait, pour pouvoir avoir un héritage en diamant, il faut prendre deux choses en compte:
    1. Les premières classes qui héritent directement de la classe ancêtre doivent avoir un héritage virtuel
    2. Toutes les classes dérivées doivent appeler explicitement le constructeur de la classe ancêtre.


    En code, cela donne 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
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    /* La classe ancêtre, celle dont toutes les autres héritent, peut etre en suivant
      *deux chemins différents
      */
    class Base
    {
        public:
            Base(/*...*/);
            virtual ~Base();
            /* ...*/
    };
    /* les classes dérivant directement de la classe ancêtres, avec un héritage virtuel
     * je montre un exemple avec deux, mais on peut en avoir plus ;)
     */
    class Derivee1 : virtual Base
    {
        public:
            Deriviee1(/* ... */ : Base(/*... */) // comme d'hab, on appel le constructeur
                                                  // de la classe parent ;)
            /*...*/
    };
     
    class Derivee2 : virtual Base
    {
        public:
            Deriviee2(/* ... */ : Base(/*... */) // comme d'hab, on appel le constructeur
                                                  // de la classe parent ;)
            /*...*/
    };
     
    class Diamant : public Derivee1, public Derivee2
    {
        public:
            /* du fait de l'héritage virtuel, la partie "ancêtre" (venue de Base)
             * ne vient ni de Derivee1, ni de Derivee2, ou, plutot, ni le 
             * constructeur de Derivee1 ni celui de Derivee2 ne prend la responsabilité
             * d'appeler celui de Base (bien qu'ils l'eussent fait si l'héritage n'avait
             * pas été virtuel)... il faut donc non seulement appeler le constructeur
             * des deux classes parent "directes", mais aussi celui de la classe ancêtre
             */
            Diamant(/* ...*/): Base(/* ... */), Derivee1(/*...*), Derivee2(/*...*/)
            {}
            /* ...*/
    };
    Cependant, comme je l'ai fait remarqué lors de ma première intervention, la présence d'un héritage en diamant est très souvent le symptome d'un problème de conception!!!

    L'exemple que tu donnes n'est, très clairement, pas conceptuellement correct pour présenter le problème.

    A vrai dire, je cherche encore vainement après un exemple dans lequel l'héritage en diamant serait, effectivement, la seule solution envisageable, et, bien que je ne me considère ni plus ni moins faillible qu'un autre, je n'en ai pas encore trouvé

    N'hésites donc pas à apprendre la technique, étant donné que c'est une solution offerte par le langage, mais, en toute honnêteté, les occasions de l'utiliser seront très loin d'être légions
    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 confirmé
    Profil pro
    Inscrit en
    Octobre 2008
    Messages
    124
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2008
    Messages : 124
    Par défaut
    Citation Envoyé par koala01 Voir le message
    En fait, pour pouvoir avoir un héritage en diamant, il faut prendre deux choses en compte:
    1. Les premières classes qui héritent directement de la classe ancêtre doivent avoir un héritage virtuel
    2. Toutes les classes dérivées doivent appeler explicitement le constructeur de la classe ancêtre.
    Merci koala01 ! C'est bien la solution de faire appel au constructeur de la classe de base dans les classes filles.

    J'avais déjà vu cette solution mais sans la retenir. J'ai fait l'erreur de ne pas lire correctement le cours sur developpez.

    Il faut aussi que je me concentre sur les designs pattern. L'apprentissage de la programmation est un univers sans fin...

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

Discussions similaires

  1. Héritage en diamant et méthodes virtuelles
    Par Neckara dans le forum C++
    Réponses: 9
    Dernier message: 06/10/2012, 17h15
  2. Question sur l'héritage en diamant
    Par f56bre dans le forum Langage
    Réponses: 3
    Dernier message: 26/09/2011, 20h31
  3. Héritage multiple & diamant de la mort
    Par oodini dans le forum C++
    Réponses: 7
    Dernier message: 16/06/2010, 14h51
  4. Héritage en diamant
    Par Chatbour dans le forum Langage
    Réponses: 5
    Dernier message: 15/08/2008, 19h36
  5. Héritage en diamant foireux...
    Par Progs dans le forum C++
    Réponses: 11
    Dernier message: 05/03/2006, 19h52

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