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 imbriqué ou héritage multiple ?


Sujet :

Langage C++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre très actif

    Homme Profil pro
    Étudiant
    Inscrit en
    Novembre 2011
    Messages
    685
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Novembre 2011
    Messages : 685
    Par défaut héritage imbriqué ou héritage multiple ?
    bonjour,

    j'ai une classe de base B abstraite (non instanciable) et des classes filles C, ainsi qu'une classe supplémentaire A. Je peux représenter leur hiérarchie de ces deux manières :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    class B : public A { virtual void foo() =0; };
    class C : public B {};
     
    class A {};
    class B { virtual void foo() =0; };
    class C : public A, public B {};
    Y'a t'il une manière à préférer ? Conceptuellement les deux se valent, la seule qui gêne un de mes collègues, c'est qu'on fasse hériter à une classe une classe abstraite qui hérite d'une classe concrète, mais moi ça ne me gêne pas, je vois plus la flemme de répéter dans toutes les classes filles l'héritage de A

  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
    Hello,

    Si les deux se valent, l'héritage imbriqué est imo préférable : si un B EST UN A alors B doit hériter de A, classe abstraite ou non.

  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,

    En fait, la question a l'air simple, mais la réponse est particulièrement compliquée parce qu'il y a de nombreux aspects à prendre en compte

    En gros, qu'il y ait deux, trois , cinq ou dix classes à prendre en compte, que certaines classes soient ou non des classes abstraites, les questions à se poser sont toujours les mêmes pour chaque relation d'héritage multiple -- multiple ou non -- que tu pourrais envisager:

    1 - Peut-on, sémantiquement parlant (comprends : vis-à vis des définitions du dictionnaire) estimer que la classe dérivée est "une sorte d'objet" du type de la classe de base

    Si la classe A est en réalité la classe Vehicule, que la classe B est en réalité la classe Poisson et que la classe C est en réalité la classe Maison, tu oublies directement toute tentative d'héritage public car ce sont des classes qui n'ont strictement aucun point commun, à par le fait de pouvoir être considérées comme des "Objets", mais je suis farouchement opposés au god objects. On n'est pas en java ou en C# ici

    2- Est-ce que toutes les propriétés prouvables de la classe qui servira de base sont des propriétés prouvables de la classe qui doit hériter de la classe de base

    Bien que ce soit un peu restrictif, tu peux déjà te demander si toutes les fonctions membres qui seront exposées par la classe de base ont du sens pour la classe dérivée.

    Vu que tu as trois classes les solutions sont :
    1. Toutes les fonctions publiques de A sont des fonctions publiques que l'on s'attend à retrouver dans B et toutes les fonctions publiques de B sont des fonctions que l'on s'attend à retrouver dans C.
    2. Toutes les fonctions publiques de A se retrouvent dans B et dans C mais il y a ne serait-ce qu'une fonction publique de B que l'on ne retrouve pas dans C.
    3. Toutes les fonctions publique de A se retrouvent dans C, toutes les fonctions publiques de B se retrouvent dans C mais il y a aucune fonction publique de A qui se retrouve dans B.
    4. Il y a dans A des fonctions publique que l'on ne retrouve ni dans B, ni dans C et il y a dans B des fonctions publiques que l'on en retrouve pas dans C
    5. J'ai l'impression d'en oublier

    En (1), tu peux aller plus loin dans la réflexion pour l'héritage de B envers A et pour l'héritage de C envers B, car toutes les propriétés prouvable de A sont des propriétés prouvables pour B et que toutes les propriétés prouvable pour B (dont celles de A) sont des propriétés prouvables pour C. (*)

    En (2), tu peux envisager de faire hériter C de A, de faire hériter B de A, mais tu ne peux pas envisager de faire hériter C de B : B et C seront peut-être des classes "soeurs" (car ayant la même classe mère), mais ne seront en tout cas pas mère et fille. (*)

    En (3) tu peux envisager de faire hériter C de B ET de A, mais tu ne peux pas envisager de faire hériter B de A.(*)

    En (4) tu ne peux envisager aucune relation d'héritage(*)

    En fait, pour le (5), ce qui manque ne ferait qu'inverser la relation d'héritage possible (permettre à B d'hériter de C et / ou à A d'hériter de B et / ou de C)

    (*) La réflexion se borne ici à une relation d'héritage limitée au paradigme OO... Certaines solutions pourraient passer par le paradigme générique, mais j'en parlerai plus tard (**)

    3 - Y a-t-il, au niveau du langage ou des "normes de codage" de l'entreprise des restrictions qui empêcherait certaines relations d'héritage

    Si restriction il y a, ce sera sans doute sous les formes d'héritage multiple . Comme on est en C++ et non en java ou en C#, ce n'est pas le langage qui placera ces restrictions, et comme tu poses la question, on peut décemment estimer que les normes de codage de ton entreprise n'ont rien contre l'héritage multiple (même si elles peuvent insister sur la vigilance qu'il faut lorsqu'on réfléchi à l'éventualité de l'utiliser ).

    Pour les relations qui ont été retenues, il reste trois vérifications qui concerne la programmation par contrat pour prendre la décision finale à faire :
    1. Les préconditions de la classe de base ne peuvent pas être adoucies dans la classe dérivée.
    2. Les postconditions de la classe dérivées ne peuvent pas être plus fortes que celles de la classe de base
    3. Les invariants doivent être respectés.
    Tu ne peux donc pas envisager de faire hériter Carre (dont on apprend à l'école que c'est un rectangle) de Rectangle, car la postcondition à toute modification du carré est beaucoup plus forte que la postcondition appliquée à une modification similaire sur un rectangle : les quatre cotés doivent être égaux.

    Et comme la postcondition testée doit être vraie en permanence, on se rend compte qu'il s'agit en réalité d'un invariant. Cet invariant prendra en réalité la forme d'impliquer le fait qu'il faut forcément deux valeurs pour représenter les quatre cotés d'un rectangle alors qu'une seule suffirait à représenter les quatre cotés d'un carré

    Tu ne peux pas envisager de faire hériter ListeTriee de liste pour la même raison (si ce n'est que la condition devient "les éléments doivent être dans un ordre particulier" ).

    (**) Après, nous avons le plaisir de développer en C++ qui est un langage qui intègre le paradigme générique et le paradigme OO de manière particulièrement souple comparé aux generics que l'on retrouve dans d'autres langages.

    Il existe donc -- peut être -- une solution alternative qui passerait par le fait de ne pas se limiter au seul paradigme OO mais de profiter du meilleur des différents mondes en envisageant d'utiliser le paradigme générique.

    Tu pourrais, par exemple, parfaitement envisager de créer une (ou plusieurs) classes template qui prendrai(en)t en charge un aspect bien particulier qui serait commun à A, B et C et pour lequel tu pourrais sans doute même fournir l'implémentation des comportements exposés.

    Par exemple, si A, B et C présentent toutes les trois la caractéristique d'être identifiables par un identifiant unique, mais que l'une des questions que tu t'es posée t'a amené à la conclusion que l'héritage public "classique" (comprends : basé sur le seul paradigme OO) n'était pas possible, il n'y a strictement rien qui t'interdise d'envisager la création d'une classe template proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    template <typename T>
    class Identifiable{
        public:
            Identifiable(/*...*/ ): id_(/*...*/){}
            size_t id() const{return id_;}
        private:
            size_t id_;
    };
    template <typename T>
    bool lessById(Identifiable<T> const & first, Identifiable<T> const & second){
        return first.id() < second.id();
    }
    Tu pourrais donc utiliser cette "interface" 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
    12
    class A : public IDentifiable<A>{
        public: 
            A(/*...*/) : Identifiable<A>(/* ... */){}
    };
    class B : public IDentifiable<B>{
        public: 
            B(/*...*/) : Identifiable<B>(/* ... */){}
    };
    class C : public IDentifiable<C>{
        public: 
            C(/*...*/) : Identifiable<C>(/* ... */){}
    };
    sans créer pour autant une relation forte entre A, B et C.

    Elles ont le point commun d'être identifiables par un identifiant unique (dont le type est identique) , tu peux utiliser lessById indifféremment entre deux A, entre deux B ou entre deux C, mais tu ne pourras jamais faire cohabiter un A avec autre chose qu'un A dans une collection d'objet, et tu ne pourras jamais faire passer un B ou un C pour autre chose qu'un B ou un C
    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 très actif

    Homme Profil pro
    Étudiant
    Inscrit en
    Novembre 2011
    Messages
    685
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Novembre 2011
    Messages : 685
    Par défaut
    1 - Oui, je penses. Cf. definitions précises plus bas, mais la classe de base B représente une espèce de contrat que doivent remplir toutes les classes spécialisées C. Cela fait de B un A avec un invariant supplémentaire à respecter pour C.

    2 -

    • A est une palette flottante d'actions (style la fenetre "outil" flottante dans Paint)
    • C est un A qui doit obligatoirement fournir un service "déclencheMoi" et une "action principale", une action par défaut en quelque sorte
    • B est donc la base commune, décrite juste à l'instant, à toutes les palettes spécialisées de cette manière
    • D est un A, composé de plusieurs C, et chaque action de A est l'action principale d'un C (cette action principale peut changer à l'éxecution)


    Tout ceci pour obtenir une palette d'outils style paint, qui ont des "onglets" dédiés aux fonctionnalités qu'ils représentent.

    Toutes les fonctions publiques de A sont des fonctions publiques que l'on s'attend à retrouver dans C, et les deux fonctions virtuelles pures de B (B ne contient que cela, en plus de deux constructeurs) doivent absolument être implémentées par C, même si un comportement par défaut est défini dans B, au cas où.

    Du coup je ne suis pas trop sur de voir à quel cas cela se rapproche le plus, mais on dirais (5) (ou (3) mais vu que la fin de ta phrase est ill-formed , pas sur que ça corresponde et puis la conclusion ne semble pas concorder)

    Me trompé-je ?

  5. #5
    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 Kaamui Voir le message
    Toutes les fonctions publiques de A sont des fonctions publiques que l'on s'attend à retrouver dans C, et les deux fonctions virtuelles pures de B (B ne contient que cela, en plus de deux constructeurs) doivent absolument être implémentées par C, même si un comportement par défaut est défini dans B, au cas où.
    Alors, la question qui tue : Y a-t-il du sens (et surtout une obligation) à faire hériter B de A

    On a bien compris que tu veux pouvoir substituer un C à un A et que tu veux aussi pouvoir substituer un C à un B.

    Mais veux-tu / dois tu pouvoir substituer un B à un A

    On pourrait poser la question différemment : Mettons que dans trois mois, tu aies besoin d'une classe E qui hérite de B. Y a-t-il le moindre sens de permettre à E d'être un B sans lui permettre d'être un A

    Si oui, l'héritage multiple sera la solution la mieux adaptée (parce que, au pire, E hérite de A et de B, si tu veux disposer des possibilités des deux).

    Si tu estimes que, quoi qu'il advienne, E doit être un A dés le moment où elle hérite de B, alors la solution la plus adaptée sera sans doute de faire hériter directement B de A

    Notes que, dans un cas comme dans l'autre, la décision peut parfaitement être réévaluée par la suite (du moins, tant qu'il n'y a pas *trop* de classes qui héritent de B )
    Du coup je ne suis pas trop sur de voir à quel cas cela se rapproche le plus, mais on dirais (5) (ou (3) mais vu que la fin de ta phrase est ill-formed , pas sur que ça corresponde et puis la conclusion ne semble pas concorder)
    J'ai corrigé, ca te semble plus clair
    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

  6. #6
    Expert confirmé

    Inscrit en
    Novembre 2005
    Messages
    5 145
    Détails du profil
    Informations forums :
    Inscription : Novembre 2005
    Messages : 5 145
    Par défaut
    Alors, la question qui tue : Y a-t-il du sens (et surtout une obligation) à faire hériter B de A?
    Si les justifications sont des raisons d'implementation, il y a la possibilite d'introduire une classe heritant de A et de B dont les C heritent sans qu'il y ait de liens entre A et B.

  7. #7
    Membre très actif

    Homme Profil pro
    Étudiant
    Inscrit en
    Novembre 2011
    Messages
    685
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Novembre 2011
    Messages : 685
    Par défaut
    Ta question qui tue était la bonne

    En effet j'aurais du me poser la question dans ce sens, car je me rend compte que B sans A n'est plus rien, en tout cas on ne peut pas être un B sans être un A aussi


    Oui après ta correction c'est mieux et donc on n'est pas dans ce cas là non plus.

    Et donc la solution d'un intermédiaire vaudrait mieux ?

    B est une palette flottante qui implémente obligatoirement les deux fonctions virtuelles pures. C n'est que sa dérivée instanciable avec les mêmes invariants, B me garantissant juste que ces deux méthods seront implémentées.

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

Discussions similaires

  1. Templates de classes imbriquées et héritage
    Par progcyb dans le forum Langage
    Réponses: 0
    Dernier message: 18/11/2014, 21h54
  2. Multi héritage Qt -> multi héritage de QWidget ?
    Par gassi64 dans le forum Débuter
    Réponses: 2
    Dernier message: 24/07/2009, 11h33
  3. Héritage, lib, Solutions avec multiples projets
    Par Ikit dans le forum Windows Presentation Foundation
    Réponses: 5
    Dernier message: 22/04/2008, 12h01
  4. Réponses: 4
    Dernier message: 22/12/2004, 14h28

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