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 :

Petite problématique d'héritage/polymorphisme


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
    Mars 2008
    Messages
    118
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2008
    Messages : 118
    Par défaut Petite problématique d'héritage/polymorphisme
    Bonjour, je recherche une solution à la situation suivante:

    J'ai un classe abstraite A déclarant plusieurs méthodes virtuelle pures, qui me sert d'interface.

    J'ai une première classe B dérivant de A et qui gère en interne des ressources qui doivent absolument rester unique dans mon projet.

    Et j'ai une seconde classe C, qui implémente différemment l'interface A, mais qui doit se servir des même ressources que B.


    Ma première idée (et qui reste ma solution par défaut, si je ne trouve pas mieux), est de faire dériver C de A, et de mettre en relation B et C à l'initialisation, afin qu'elles se partagent la ressource (J'aurai C qui référencera les ressources de B, et B qui en gardera la responsabilité).

    Mais comme C partage, non seulement les ressources de B, mais également beaucoup de code interne très proches (plusieurs sous-méthodes privés identiques), j'aurai trouvé plus élégant de faire directement dériver C de B.

    La ressource devant rester unique, je n'instancierai dans mon projet qu'un seul objet C, mais j'aimerai en obtenir une seconde référence, qui présenterait l'implémentation de B.
    C'est à dire que lorsque je rencontre une fonction prenant un pointeur A, j'aimerai en faire un premier appel qui lancera l'implémentation C (type réel de l'objet instancié) et un second appel qui lancera l'implémentation de B (un de ses types parents).

    Butant dans ma réflexion, j'ai testé méthodiquement tous les types de cast (static, dynamic et reinterpret), ainsi qu'avec et sans 'virtual' dans la classe A, mais rien ne m'a permit d'atteindre l'implémentation de B.
    A l'intérieur de C, j'arrive à accédé à une méthode de B par B::foo(), mais existe-t-il une syntaxe qui m'aurait échappé, permettant d'obtenir une référence à mon objet C appelant automatiquement l'implémentation de B, lors d'appel de type foo(A* obj) ?



    Merci

  2. #2
    Membre confirmé
    Profil pro
    Inscrit en
    Mars 2008
    Messages
    118
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2008
    Messages : 118
    Par défaut
    Voici un petit code pour mieux illustrer ce que je cherche à faire
    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
     
    class base{
        base() {}
        ~base(){}
     
        virtual void print() { std::cout << "A"; }
    };
     
    class derive : public base{
        derive() {}
        ~derive() {}
     
        void print() { std::cout << "B"; }
    }
     
    void print(base *obj){
        obj->print();
    }
     
    int main(){
        derive *objB = new derive();
        base *objA = objB;
     
        print(objA);
        print(objB);
     
    }
    Je souhaite qu'il m'imprime "base" et "derive", mais pour le moment je ne parvient qu'à imprimer 2x "derive".

    Puisqu'à l'intérieur de la classe derive, j'arrive à atteindre le print de la classe de base par un base::print();, j'ai même tenter ceci depuis le main():
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    std::function<void(void)> f = std::bind(&base::print, ObjB);
    f();
    Mais même lui m'imprime "derive". De toute façon j'imagine que j'aurai eu du mal à utiliser cette syntaxe dans mon projet.


    Finalement, si je ne dis pas trop de bêtise, ce que je cherche à faire c'est d'obtenir une référence de ObjB qui aurait sa propre vtable. Ou au moins de pouvoir spécifier à print(base *obj) quelle vtable utiliser, lors de ses appels.
    J'imagine que C++ ne permet pas de réaliser cela?
    Intrinsèquement, la classe derive doit bien posséder les définitions de la classe de base (c'est confirmé, puisque je parviens à faire des appels base::print(), à l'intérieur d'elle-même). C'est dommage qu'il n'est pas possible d'atteindre ces définitions depuis "le monde extérieur".



    Pour en revenir à mon problème de base (classe A, B, C), je vais mettre mes ressources et les méthodes protégés associés en static, et créer deux instances distinctes, de B et de C.

    Mais si quelqu'un a des commentaires à faire ça m'intéresserait bien.
    Merci.

  3. #3
    Rédacteur/Modérateur


    Homme Profil pro
    Network game programmer
    Inscrit en
    Juin 2010
    Messages
    7 151
    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 151
    Billets dans le blog
    4
    Par défaut
    Je n'ai rien compris au problème mais c'est le principe même de l'héritage et du virtual dont tu parles...
    Le but est d'appeler la méthode de l'objet réel, depuis un objet de base*.
    Si tu veux forcer A::print, tu peux faire ptr->A::print. Enfin, si tenté que print soit public.
    Mais bon, au mieux tu as pas besoin de l'héritage, au pire tu n'en as pas compris l'utilisation.
    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.

  4. #4
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2009
    Messages
    4 493
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Loire Atlantique (Pays de la Loire)

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

    Informations forums :
    Inscription : Juin 2009
    Messages : 4 493
    Billets dans le blog
    1
    Par défaut
    B possède des ressources uniques qu'elles doivent gérer elle-même ; mais C doit accéder à ses ressources. Pour moi, il y a une contradiction : ces ressources ne sont pas franchement réservées à B si C y accède

    N'as-tu pas plutôt besoin d'une classe R pour gérer les ressources (notamment l'unicité) ?

    B prendrait en paramètre une référence sur R quand C prendrait en paramètre un const-référence sur R ?

    Si B et C partagent bcp de code, tu peux toujours faire une classe intermédiaire avec l'implémentation commune :
       A
       |
       |
    Common
    |    |
    |    |
    B    C

  5. #5
    Membre confirmé
    Profil pro
    Inscrit en
    Mars 2008
    Messages
    118
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2008
    Messages : 118
    Par défaut
    Citation Envoyé par Bktero Voir le message
    B possède des ressources uniques qu'elles doivent gérer elle-même ; mais C doit accéder à ses ressources. Pour moi, il y a une contradiction : ces ressources ne sont pas franchement réservées à B si C y accède
    D'où mon idée de faire dériver C de B. Comme ça C aurait posséder les ressources, par le biais de son héritage, et aurait ainsi pu accéder aux données utiles, mais le code de gestion (pour faire simple, 'ouverture'/'fermeture' même si c'est pas vraiment de ça qu'il s'agit) aurait été implémenté dans B et rester de son seul ressort.



    Citation Envoyé par Bktero Voir le message
    N'as-tu pas plutôt besoin d'une classe R pour gérer les ressources (notamment l'unicité) ?

    B prendrait en paramètre une référence sur R quand C prendrait en paramètre un const-référence sur R ?
    A la base, j'avais beaucoup de mal à me résoudre à cette approche, mais au final c'est effectivement là-dessus que je suis parti, plutôt que d'opter pour des membres et données statiques.
    Il y a une intégration très profonde entre B/C et ces ressources. Et c'était plutôt cohérent, conceptuellement, que ces ressources soit géré par B et/ou C. Elles sont sur un même pied d'égalité et l'un comme l'autre aurai pu s'en occuper. Le seule problème c'était l'unicité, il ne fallait qu'il n'y en ait qu'un à s'en occuper.
    Mais j'ai finalement réussi à faire une découpe plutôt propre. Niveau encapsulation c'est pas top, puisque B et C accède à la totalité des membres de la nouvelle classe qui gère la ressource. Mais bon, au moins ça passe par des accesseurs.

    Et au final, une fois de code lié aux ressources retiré, C n'est même plus besoin de dérivé de B. Elles n'ont aujourd'hui presque plus aucun code en commun.

  6. #6
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2009
    Messages
    4 493
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Loire Atlantique (Pays de la Loire)

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

    Informations forums :
    Inscription : Juin 2009
    Messages : 4 493
    Billets dans le blog
    1
    Par défaut
    L'héritage est une manière pratique de récupérer du comportement mais il faut revenir à la sémantique de l'héritage.

    L'héritage public modélise une relation "est un". Si B hérite de C, alors cela veut dire que C est un B. Pas juste que C se comporte un peu / en partie / beaucoup comme un B. L'héritage public est une relation très forte, la sémantique n'est pas anodine.

    Il y a d'autres façon de récupérer du comportement sans avoir cette sémantique de "est un". Il y a l'héritage privé qui a une sémantique de "est implémenté en terme de". Par exemple, tu as une classe List et tu veux gérer un groupe de personne pour ta classe Group, peut-être que Group va hériter de manière privée de List. Il y a surtout l'encapsulation : Group possède une List.

    L'héritage public ne doit pas être la relation entre 2 classes qui doit te venir à l'idée. On apprend à tous les débutants "la POO c'est trop bien tu peux faire de l'héritage" mais les vieux sages savent que la POO c'est beaucoup de choses avant l'héritage.

    N'oublie pas non plus qu'une classe doit avoir une seule responsabilité : https://en.wikipedia.org/wiki/Single...lity_principle

  7. #7
    Membre confirmé
    Profil pro
    Inscrit en
    Mars 2008
    Messages
    118
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2008
    Messages : 118
    Par défaut
    Citation Envoyé par Bousk Voir le message
    Je n'ai rien compris au problème mais c'est le principe même de l'héritage et du virtual dont tu parles...
    Le but est d'appeler la méthode de l'objet réel, depuis un objet de base*.
    Si tu veux forcer A::print, tu peux faire ptr->A::print. Enfin, si tenté que print soit public.
    Mais bon, au mieux tu as pas besoin de l'héritage, au pire tu n'en as pas compris l'utilisation.
    Ah super, c’est cette syntaxe là que je ne connaissais pas. Mais finalement quand je la regarde, je me rend compte que je ne pourrai pas l’utiliser.

    Mon but est d’utiliser le polymorphisme, non pas en appelant l’implémentation final de l’objet, mais une de ses implémentations intermédiaires, dans sa hiérarchie d’héritage.

    Sur un objet c dérivé de ceci : A->B->C,
    En le passant deux fois de suite à une fonction de prototype foo(A*), j’aurai aimé qu’il m’imprime une première fois « C », puis une seconde fois « B », en se faisant passer pour un de ses parents (avec un cast quelconque).
    Aujourd’hui, en mettant ‘virtual’ dans A, j’obtiens « C » et « C », et en retirant le ‘virtual’ j’ai « A » et « A ». Mais jamais je n’arrive à atteindre « B ».

    Ca aurait été sympa si il aurait été possible (pour des cas très exceptionnel et bien alambiqué) de paramétrer un objet ou une référence pour qu’il se comporte, non pas pour ce qu’il est vraiment, mais comme l’un de ses parent, lors de l'utilisation du polymorphisme.
    Avec l’héritage, il possède bien l’intégralité des méthodes de sa hiérarchie, alors pourquoi pas ?


    Comme le C++ hérite du C, je me suis dit que je pouvais peut-être m’en sortir avec un cast C bien dégueu et unsafe sur un pointer, mais rien. Et j’ai bien compris que c’est la vtable qui m’empêche de faire ça.
    Alors peut-être avec un hardcopy de mon objet, en définissant une nouvelle vtable pointant sur les méthodes que je souhaite, tout en référençant les même données originales… mais je vais arrêter là les conneries.


    [edit]
    En résumé, j'aurai souhaité faire ceci:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    foo(A* obj);
     
    C c;
     
    foo( (B*)&c );
    Et que ça m'imprime "B" et non pas "C" ou "A"

Discussions similaires

  1. Petit projet pour héritage & polymorphisme
    Par proginfme dans le forum Débuter avec Java
    Réponses: 10
    Dernier message: 21/02/2015, 21h41
  2. [débutant] héritage, polymorphisme, etc !
    Par remsrock dans le forum C#
    Réponses: 4
    Dernier message: 31/10/2008, 12h33
  3. Problème d'héritage - polymorphisme
    Par antoine2405 dans le forum Langage
    Réponses: 16
    Dernier message: 28/04/2008, 13h51
  4. Réponses: 6
    Dernier message: 08/02/2008, 14h58
  5. [Héritage] [Polymorphisme] Question de débutant ?
    Par TSnarfK dans le forum Langage
    Réponses: 9
    Dernier message: 12/09/2006, 15h39

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