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 :

héritage et polymorphisme info ?


Sujet :

C++

  1. #1
    Membre à l'essai
    Profil pro
    Inscrit en
    Août 2009
    Messages
    42
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Août 2009
    Messages : 42
    Points : 23
    Points
    23
    Par défaut héritage et polymorphisme info ?
    salut a tous j'ai besoin de ca :

    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
     
    class base
    {
    public:
    void funcCommune(void);
    int paramCommun;
    }
     
    class enfantA : base
    {
      enfantA(){}
      ~enfantA(){}
    }
     
    class enfantB : base
    {
      enfantB(){}
      ~enfantB(){}
    }
     
    class enfantC : base
    {
      enfantC(){}
      ~enfantC(){}
    }
    voila pour la déclaration et j'aurais besoin d'avoir une fonction qui a une classe return "base"

    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
     
    :
    class autreOBJ
    {
    .
    .
    .
     base funtion_retour_base()
     {
      enfantA objectA;
      return (base) objectA;
     }
    .
    .
    .
    }
    voila ... je ne sais pas si j'ai été clair :s mais c'est ce dont j'ai besoin .

    voici donc ma question : Est ce que je procède de la bonne façon ou y a-t-il un autre/meilleur moyen de faire ce que je veux ?

  2. #2
    Membre émérite
    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    2 764
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2004
    Messages : 2 764
    Points : 2 704
    Points
    2 704
    Par défaut
    Tout d'abord, tu n'a spas besoin de définir le constructeur/destructeur pour les classes que tu présentes. C++ le fait pour toi.

    Ensuite, sur ta fonction, tu n'as pas besoin de transtyper objectA en base, puisque objectA est intrinsèquement un objet de type base, car il en dérive.

    En outre, si le transtypage avait été nécessaire, il eût été préférable d'utiliser static_cast<base>(objectA), plutôt que ce transtypage à la C.

  3. #3
    Membre expérimenté
    Homme Profil pro
    Inscrit en
    Décembre 2010
    Messages
    734
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Décembre 2010
    Messages : 734
    Points : 1 475
    Points
    1 475
    Par défaut
    Petite question, est-ce que ce n'est pas un cas où on risque un slicing? Il faudrait que ce soit un (smart) pointer, non?
    @kev: je crois qu'en renvoyant un objet comme tu le fais tu risque un problème car il sera fait une recopie dans un nouvel objet de type base qui ne tiendra pas compte du type effectif de l'objet d'origine renvoyé, avec perte des données spécifiques du type enfantA, et de l'information du type effectif enfantA

  4. #4
    Membre émérite
    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    2 764
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2004
    Messages : 2 764
    Points : 2 704
    Points
    2 704
    Par défaut
    Citation Envoyé par therwald Voir le message
    Petite question, est-ce que ce n'est pas un cas où on risque un slicing? Il faudrait que ce soit un (smart) pointer, non ?
    Si , mais ce n'est pas forcément gênant. S'il n'a besoin que des fonctionnalités du type base...

    Citation Envoyé par therwald Voir le message
    @kev: je crois qu'en renvoyant un objet comme tu le fais tu risque un problème car il sera fait une recopie dans un nouvel objet de type base qui ne tiendra pas compte du type effectif de l'objet d'origine renvoyé, avec perte des données spécifiques du type enfantA, et de l'information du type effectif enfantA
    Oui, mais comme manifestement, il veut un objet de type base, et se contrefiche de ce que rajoutent ses classes dérivées, il n'y a pas de problème.

  5. #5
    Membre expérimenté
    Homme Profil pro
    Inscrit en
    Décembre 2010
    Messages
    734
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Décembre 2010
    Messages : 734
    Points : 1 475
    Points
    1 475
    Par défaut
    C'est ce dont je voudrais m'assurer...peut-être n'a-t-il pas conscience du problème...pour quoi sous-typer si tu n'utilises pas la polymorphie?
    Il s'attend peut-être à voir jouer ses redéfinitions, ce qui ne sera pas le cas avec ce code.
    Mais il n'y a que lui qui a la réponse à cette question.

  6. #6
    Membre expérimenté
    Homme Profil pro
    Inscrit en
    Décembre 2010
    Messages
    734
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Décembre 2010
    Messages : 734
    Points : 1 475
    Points
    1 475
    Par défaut
    Je suis tombé sur ceci
    je vais créer un post concernant l'héritage et le polymorphisme avec une version théorique
    au détour de ça:
    http://www.developpez.net/forums/d11...iation-classe/
    La méthode serait donc une factory method, et là on ne veut surtout pas perdre le type originel, à mon avis.
    Donc non, pour moi ce n'est pas bon, il faut renvoyer un smart-pointer sur le type de base et non une copie dans le type de base. Et utiliser une allocation dynamique dans la factory_method...et résoudre le problème de la durée de vie de l'instance.

  7. #7
    Membre expert
    Profil pro
    Inscrit en
    Mars 2007
    Messages
    1 415
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Mars 2007
    Messages : 1 415
    Points : 3 156
    Points
    3 156
    Par défaut
    Citation Envoyé par therwald Voir le message
    Petite question, est-ce que ce n'est pas un cas où on risque un slicing? Il faudrait que ce soit un (smart) pointer, non?
    @kev: je crois qu'en renvoyant un objet comme tu le fais tu risque un problème car il sera fait une recopie dans un nouvel objet de type base qui ne tiendra pas compte du type effectif de l'objet d'origine renvoyé, avec perte des données spécifiques du type enfantA, et de l'information du type effectif enfantA
    C'est tout à fait exact : tu tentes de mélanger polymorphisme et sémantique de valeur, ce qui ne cohabite pas bien ensemble !

    Cela ne fonctionne pas, car tu tentes d'effectuer une copie depuis enfantA vers un objet base qui ne peut rien contenir de plus qu'un objet base. L'objet sera tronqué et tu risques de te retrouver face à des comportements ingérables. Ce code par exemple, qui compile pourtant sans erreur, le montre :

    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
    #include <iostream>
     
    class base
    {
      int paramCommun;
     
    public:
      base() : paramCommun(5)
      {}
     
      virtual int funcCommune(void)
      {
        return paramCommun;
      }
    };
     
    class enfantA : public base
    {
      int paramPasCommun;
     
    public:
      enfantA() : base(), paramPasCommun(10)
      {}
     
      virtual int funcCommune(void)
      {
        return paramPasCommun;
      }
    };
     
    struct autreOBJ
    {
     base funtion_retour_base()
     {
      enfantA objectA;
      return objectA; // L'objet objectA est tronqué et "paramPasCommun" est perdu !
     }
    };
     
    int main(int argc, char* argv[])
    { 
      enfantA enfantNormal;
      std::cout << enfantNormal.funcCommune() << std::endl; // Affiche 10 comme prévu
     
      autreOBJ obj;
      base enfantPasNormal = obj.funtion_retour_base();
      std::cout << enfantPasNormal.funcCommune() << std::endl; // Affiche 5, ce qui n'est pas correct !
    }
    Le polymorphisme est inutilisable et dangereux ! Soit tu n'as pas besoin de paramètres propres aux classes enfant, ni de fonctions virtuelles surchargées dans les enfants, et dans ce cas, tu n'as pas besoin de faire de l'héritage et ton problème doit être résolu autrement, soit tu as besoin du polymorphisme, et voici un code adapté :

    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
    #include <iostream>
     
    class base
    {
      int paramCommun;
     
    public:
      base() : paramCommun(5)
      {}
     
      virtual int funcCommune(void)
      {
        return paramCommun;
      }
    };
     
    class enfantA : public base
    {
      int paramPasCommun;
     
    public:
      enfantA() : base(), paramPasCommun(10)
      {}
     
      virtual int funcCommune(void)
      {
        return paramPasCommun;
      }
    };
     
    struct autreOBJ
    {
     base * funtion_retour_base()
     {
      enfantA * pObjectA = new enfantA();
      return pObjectA; // L'objet objectA est tronqué et "paramPasCommun" est perdu !
     }
    };
     
    int main(int argc, char* argv[])
    { 
      enfantA enfantNormal;
      std::cout << enfantNormal.funcCommune() << std::endl; // Affiche 10 comme prévu
     
      autreOBJ obj;
      base * pAutreEnfant = obj.funtion_retour_base();
      if(pAutreEnfant)
      {
        std::cout << pAutreEnfant -> funcCommune() << std::endl; // Affiche 10, ce qui est correct
        delete pAutreEnfant; // On libère la mémoire
        pAutreEnfant = 0;
      }
    }
    Si tu utilises le polymorphisme, gare à la gestion de la mémoire. La durée de vie de l'instance, comme dit thewald.

    PS : Tu as oublié de déclarer publiquement l'héritage, ce qui a du te poser problème et te pousser à venir poster ici, car l'héritage est privé par défaut et le code ne compile alors pas.
    Find me on github

  8. #8
    Membre émérite
    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    2 764
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2004
    Messages : 2 764
    Points : 2 704
    Points
    2 704
    Par défaut
    Citation Envoyé par jblecanard Voir le message
    Soit tu n'as pas besoin de paramètres propres aux classes enfant, ni de fonctions virtuelles surchargées dans les enfants, et dans ce cas, [B] tu n'as pas besoin de faire de l'héritage
    Si tu ne te bases que sur le message de ce fil, je ne suis pas d'accord.
    Une autre fonction pourrait avoir ces besoin, mais pas celle-ci.

  9. #9
    Membre expérimenté
    Homme Profil pro
    Inscrit en
    Décembre 2010
    Messages
    734
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Décembre 2010
    Messages : 734
    Points : 1 475
    Points
    1 475
    Par défaut
    Citation Envoyé par oodini Voir le message
    Si tu ne te bases que sur le message de ce fil, je ne suis pas d'accord.
    Une autre fonction pourrait avoir ces besoin, mais pas celle-ci.
    Le code est visiblement très réduit et démuni de sens. Si on veut une instance de base, et que la classe base a du sens en elle-même, POURQUOI aller instancier un objet de type enfantA?

  10. #10
    Membre émérite
    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    2 764
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2004
    Messages : 2 764
    Points : 2 704
    Points
    2 704
    Par défaut
    Citation Envoyé par therwald Voir le message
    Le code est visiblement très réduit et démuni de sens. Si on veut une instance de base, et que la classe base a du sens en elle-même, POURQUOI aller instancier un objet de type enfantA?
    Sans doute parce que le code a été simplifié. Je ne vois pas ça comme un code réaliste.

    On pourrait avoir un vecteur d'objet de type enfantA. On va en récupérer un, puis on va le rebalancer en sortie de la fonction comme un objet de type base, parce que l'appelant n'a pas besoin de savoir de quel type dérivé il s'agit.

  11. #11
    Membre expérimenté
    Homme Profil pro
    Inscrit en
    Décembre 2010
    Messages
    734
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Décembre 2010
    Messages : 734
    Points : 1 475
    Points
    1 475
    Par défaut
    Je pencherais plutôt du côté de jblecanard, je pense que même dans ce cas ce genre de code est dangereux car il peut pousser à l'erreur, un jour où l'autre on se demandera pourquoi on n'arrive pas à avoir le comportement lié au type effectif...

  12. #12
    Membre émérite
    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    2 764
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2004
    Messages : 2 764
    Points : 2 704
    Points
    2 704
    Par défaut
    Quand tu utilises une fonction qui te renvoie une instance d'un objet de base, et non pas un pointeur, pourquoi t'attendrais-tu donc à un comportement défini dans des classes dérivées ??

  13. #13
    Membre expérimenté
    Homme Profil pro
    Inscrit en
    Décembre 2010
    Messages
    734
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Décembre 2010
    Messages : 734
    Points : 1 475
    Points
    1 475
    Par défaut
    Parce que quand on crée une hiérarchie c'est pour ça. Sinon, pour la réutilisation de code on utilise de la composition ou autre...

  14. #14
    Membre émérite
    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    2 764
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2004
    Messages : 2 764
    Points : 2 704
    Points
    2 704
    Par défaut
    Citation Envoyé par therwald Voir le message
    Parce que quand on crée une hiérarchie c'est pour ça. Sinon, pour la réutilisation de code on utilise de la composition ou autre...
    Je ne suis pas d'accord. Les notions d'héritage et de polymorphisme ne sont pas nécessairement confondues. Et je ne vois pas pourquoi elles devraient l'être.

  15. #15
    En attente de confirmation mail

    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Août 2004
    Messages
    1 391
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 33
    Localisation : France, Doubs (Franche Comté)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 1 391
    Points : 3 311
    Points
    3 311
    Par défaut
    (On pare d'héritage publique ici, cependant dans le ode de l'OP celui-ci effectue des héritages privés, volontaire ou non ?)

    Ces deux notions sont confondues (du moins fortement liées) car l'héritage publique est la solution offerte par le C++ pour offrir le LSP. Si je constuit un objet appartenant à une hiérarchie je m'attend à ce que son utilisation rende compte du comportement définie dans le type réel, sans ca tout la sémantique associé à mon objet est perdue. Et avec le slicing c'est ce qui se passe : on perd le type réel, raison pour laquelle on déconseille une sémantique de valeur (constructeur de copie en particulier) avec une héritage publique (ce qui évite les cas de slicing).

    L'utilisateur du code n'a peut-être pas besoin de connaitre le type réel ni d'utiliser les services propres à celui-ci. Cependant il s'attend à ce que le comportement (pour les services du type de base) associé à son objet soit bien celui attendue, en particulier qu'il soit identique à celui qu'il était à sa création (ce qui n'est pas le cas avec le slicing).

  16. #16
    Membre émérite
    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    2 764
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2004
    Messages : 2 764
    Points : 2 704
    Points
    2 704
    Par défaut
    Citation Envoyé par Flob90 Voir le message
    Ces deux notions sont confondues (du moins fortement liées) car l'héritage public est la solution offerte par le C++ pour offrir le LSP. Si je construis un objet appartenant à une hiérarchie je m'attend à ce que son utilisation rende compte du comportement définie dans le type réel, sans ça tout la sémantique associé à mon objet est perdue. Et avec le slicing c'est ce qui se passe : on perd le type réel, raison pour laquelle on déconseille une sémantique de valeur (constructeur de copie en particulier) avec une héritage publique (ce qui évite les cas de slicing).
    Cela n'est gênant que lorsque la hiérarchie repose effectivement sur le polymorphisme. Mais ce n'est pas toujours le cas. Si aucune fonction n'est déclarée en virtuel, je ne vois pas en quoi le slicing peut poser problème.

  17. #17
    Membre expérimenté
    Homme Profil pro
    Inscrit en
    Décembre 2010
    Messages
    734
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Décembre 2010
    Messages : 734
    Points : 1 475
    Points
    1 475
    Par défaut
    Dans ce cas à mon sens la hiérarchie est vide de sens, autant déclarer n classes indépendantes. Si on ne fait que partager des données et un peu de code, autant faire une composition avec un type qui embarque ces points communs. C'est plus clair.

  18. #18
    Membre émérite
    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    2 764
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2004
    Messages : 2 764
    Points : 2 704
    Points
    2 704
    Par défaut
    basic_ios dérive de ios_base, et pourtant, il n'y a pas de méthode virtuelle.

  19. #19
    Membre expérimenté
    Homme Profil pro
    Inscrit en
    Décembre 2010
    Messages
    734
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Décembre 2010
    Messages : 734
    Points : 1 475
    Points
    1 475
    Par défaut
    Il n'en reste pas moins que si tu le slice tu te retrouves avec un objet inutile...
    EDIT: et c'est l'hypothèse optimiste, parce qu'utiliser un objet avec des comportements de sa classe de base peut amener des bugs laids et difficiles à comprendre

  20. #20
    En attente de confirmation mail

    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Août 2004
    Messages
    1 391
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 33
    Localisation : France, Doubs (Franche Comté)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 1 391
    Points : 3 311
    Points
    3 311
    Par défaut
    Le sens même d'un héritage publique c'est "IS-A", si il n'y a aucun comportement en commun il est assez difficile de respecter cette relation "IS-A" (si on respecte une relation "IS-A" c'est bien qu'on partage l'ensemble des comportements de la classe mère).

    Il reste le cas où l'on ne ferais que rajouter des comportements sans en redéfinir, mais dans ce cas je pense que l'ensemble des comportements doit se découper de manière orthogonale, et que d'autres classes peuvent profiter (par héritage publique) de ces comportements sans qu'ils aient un sens réel a eux seul (classes de politique (*)).

    (*) Une classe de politique s'utilise souvent par héritage publique (on peut supposer avoir à traiter un ensemble d'objet offrant tous ce comportement) mais une intance de classe de politique qui ne serait pas un sous-objet n'aurait pas de sens (destructeur protégé => pas de Slicing).

Discussions similaires

  1. héritage et polymorphisme
    Par julien.metais dans le forum Hibernate
    Réponses: 3
    Dernier message: 17/05/2009, 10h58
  2. Réponses: 10
    Dernier message: 17/07/2008, 21h01
  3. héritage et polymorphisme
    Par davdou dans le forum JSF
    Réponses: 2
    Dernier message: 23/11/2007, 10h51
  4. [C#] Information sur héritage et polymorphisme
    Par LE NEINDRE dans le forum C#
    Réponses: 21
    Dernier message: 14/06/2007, 12h00
  5. Réponses: 19
    Dernier message: 05/06/2007, 09h13

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