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++/CLI Discussion :

Polymorphisme d'une sous classe vs. un membre de classe


Sujet :

C++/CLI

  1. #1
    Membre du Club
    Inscrit en
    Février 2013
    Messages
    92
    Détails du profil
    Informations forums :
    Inscription : Février 2013
    Messages : 92
    Points : 49
    Points
    49
    Par défaut Polymorphisme d'une sous classe vs. un membre de classe
    Bonjour à toutes et à tous,

    Je débute en C++, et je souhaiterais savoir comment appeler la bonne fonction lorsque je déclare un membre de classe d'un type 'Base' (superclasse) et que je lui assigne une sous-classe dérivée.
    Une illustration:

    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
    Class Animal {};
     
    Class Chat:public Animal{
      public: 
        foo_chat();
    };
     
    Class Chien:public Animal{
      public: 
        foo_chien();
    };
     
    Class Simulation {
      private:
        Animal mon_animal;
        Un_type_sympa T;
     
      public:
        int main(void){
          mon_animal = Chat();
          T.compte_les_poils_de(mon_animal);
        }
    }
    avec:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    class Un_type_sympa{
      public:
        int compte_les_poils_de(Animal a) {return 0};
        int compte_les_poils_de(Chien a) {return 10};
        int compte_les_poils_de(Chat a) {return 20};
    };

    Dans mon exemple, c'est toujours 0 qui est retourné par compte_les_poils_de(), ce qui est normal, vu que mon_animal est déclaré en tant que Animal, mais comment faire pour que Un_type compte le bon nombre de poils en fonction de la classe (Chien, Chat?). Je peux m'en sortir par des conditions if else, et caster mais je suis certain qu'une solution plus élégante existe. J'ai regardé les fonctions virtuelles, les pointeurs, etc. mais je n'ai pas réussi à faire fonctionner comme il faut.

    Merci!

  2. #2
    Expert éminent sénior
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Février 2005
    Messages
    5 073
    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 073
    Points : 12 119
    Points
    12 119
    Par défaut
    Il y a plein de moyens, mais pourquoi avoir plusieurs méthodes ???

    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
    Class Animal {
    protected :
        int m_poils = 0;
    public :
        int GetPoils(){return m_poils;}
    };
     
    Class Chat:public Animal{
      public: 
        Chat:m_poils{20}();
    };
     
    Class Chien:public Animal{
      public: 
        Chien:m_poils{10}();();
    };
     
    class Un_type_sympa{
      public:
        int compte_les_poils_de(Animal a) {return a.GetPoils();};
    };
     
    Class Simulation {
      private:
        Animal mon_animal;
        Un_type_sympa T;
     
      public:
        int main(void){
          mon_animal = Chat();
          T.compte_les_poils_de(mon_animal);
        }
    }
    Si vous voulez vraiment vous la péter avec de la résolution dynamique de méthode :

    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
    Class Animal {
    protected :
    public :
        virtual int GetPoils(){return 0;}
    };
     
    Class Chat:public Animal{
      public: 
        int GetPoils() override{return 20;}
    };
     
    Class Chien:public Animal{
      public: 
        int GetPoils() override{return 10;}
    };
     
    class Un_type_sympa{
      public:
        int compte_les_poils_de(Animal a) {return a.GetPoils();};
    };
     
    Class Simulation {
      private:
        Animal mon_animal;
        Un_type_sympa T;
     
      public:
        int main(void){
          mon_animal = Chat();
          T.compte_les_poils_de(mon_animal);
        }
    }
    P.S.: vous n'êtes absolument pas dans le bon forum :
    https://www.developpez.net/forums/f19/c-cpp/cpp/

  3. #3
    Membre du Club
    Inscrit en
    Février 2013
    Messages
    92
    Détails du profil
    Informations forums :
    Inscription : Février 2013
    Messages : 92
    Points : 49
    Points
    49
    Par défaut
    Merci pour ce retour rapide et efficace.

    Pour répondre à la question, la fonction "compte_les_poils_de" est un calcul qui dépend du type de l'animal. On peut imaginer que l'approche pour compter les poils sur un chat, un serpent, une mangouste, est différente: ca dépend de la dimension de la bête et son espèce par exemple. C'est comme ca que j'illustrerais mon problème (qui n'a que peu a voir avec les animaux à la base).
    Ce qui fait que le_Type_sympa doit bien choisir sa méthode pour procéder au calcul de manière à être optimal!

    Je peux effectivement faire comme dans les 2 exemples donnés (merci!), et implémenter le calcul dans les animaux directement.
    A titre de curiosité, comment aurais-je fais pour déplacer le problème dans Un_type_sympa (comme initialement proposé)?
    J'ai aussi une contrainte de temps d'execution (bcp d'itérations car beaucoup de paquets de poils), Est-ce que la résolution dynamique de méthode est plus efficiente?

    Merci

    Edit: ah oui mince, il faudrait que je puisse bouger le topic vers la section C++

  4. #4
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 519
    Points
    41 519
    Par défaut
    @Bacelar: Ça compile, une classe abstraite contenue par valeur?
    Je pense que tu as oublié un & quelque part.
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  5. #5
    Membre du Club
    Inscrit en
    Février 2013
    Messages
    92
    Détails du profil
    Informations forums :
    Inscription : Février 2013
    Messages : 92
    Points : 49
    Points
    49
    Par défaut
    Pour la petite information, j'ai utilisé une classe abstraite (pure) avec un pointeur (class Animal), et ca marche.
    Mais je reste intéressé par la demande initiale: si le membre est d'un type (classe de base) et que c'est en fait un objet de type classe dérivée, comment fait Un_Type_Sympa pour trouver le 'vrai' type du paramètre (cad la classe dérivée par la classe de base)?

  6. #6
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 519
    Points
    41 519
    Par défaut
    Ça ne marche qu'avec les pointeurs et références, et ça vient du fait qu'une classe contenant au moins une fonction membre virtuelle possède un pointeur caché vers ce qu'on appelle la vtable. En fait, quand tu écris ceci:
    Code C++ : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    Class Animal {
    protected :
    public :
        virtual int GetPoils(){return 0;}
    };
     
    Class Chat:public Animal{
      public: 
        int GetPoils() override{return 20;}
    };
     
    void affich(Animal *pAni)
    {
        cout << pAni->GetPoils() << endl;
    }

    Ça fait ça (avec quelques casts en plus):
    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
    Class AnimalVtbl {
        int (Animal::*GetPoils)();
    };
     
    Class ChatVtbl : public AnimalVtbl { };
     
    Class Animal {
    protected :
        AnimalVtbl * pVtbl;
    public :
        Animal();
        int GetPoils(){return 0;}
    };
     
    Class Chat:public Animal{
      public: 
        Chat();
        int GetPoils() {return 20;}
    };
     
    AnimalVtbl global_Animal_vtbl = { &Animal::GetPoils; };
    ChatVtbl global_Chat_vtbl = { &Chat::GetPoils; };
     
    Animal::Animal() : pVtbl(&global_Animal_vtbl ) { }
    Chat::Chat() : Animal(), pVtbl(&global_Chat_vtbl ) { }
     
    void affich(Animal *pAni)
    {
        cout << pAni->*(pAni->pVtbl->GetPoils)() << endl;
    }
    Mais tout ça est un détail d'implémentation.
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  7. #7
    Membre du Club
    Inscrit en
    Février 2013
    Messages
    92
    Détails du profil
    Informations forums :
    Inscription : Février 2013
    Messages : 92
    Points : 49
    Points
    49
    Par défaut
    Ok, merci!
    J'ai lu que l'utilisation de la vtable ralentit l'execution, comme j'ai une grosse contrainte de temps de calcul, une solution est-elle préférable plutôt qu'une autre?
    Merci !

  8. #8
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 519
    Points
    41 519
    Par défaut
    La vtable ça fait une indirection, mais ça peut être plus rapide qu'une logique compliquée faite pour l'éviter... À cette échelle, je dirais de ne pas t'en soucier avant d'avoir passé ton code au profileur.
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  9. #9
    Membre du Club
    Inscrit en
    Février 2013
    Messages
    92
    Détails du profil
    Informations forums :
    Inscription : Février 2013
    Messages : 92
    Points : 49
    Points
    49
    Par défaut
    Superbe, merci pour ce retour,
    Après un essai entre une version avec un mauvais design (tout dans la même classe) et quelque chose de plus travaillé (plusieurs classes) je prends environ 43% de temps d'exécution en plus, ce qui est juste énorme, mais j'utilise un vieux compilateur, je vais faire tourner ca sur un plateforme plus rapide.
    Merci!

  10. #10
    Expert éminent sénior
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Février 2005
    Messages
    5 073
    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 073
    Points : 12 119
    Points
    12 119
    Par défaut
    @Bacelar: Ça compile, une classe abstraite contenue par valeur?
    Je pense que tu as oublié un & quelque part.
    Heu, ma classe Animal n'est pas abstraite.

    Pour répondre à la question, la fonction "compte_les_poils_de" est un calcul qui dépend du type de l'animal. On peut imaginer que l'approche pour compter les poils sur un chat, un serpent, une mangouste, est différente: ca dépend de la dimension de la bête et son espèce par exemple. C'est comme ca que j'illustrerais mon problème (qui n'a que peu a voir avec les animaux à la base).
    Et ?
    La 2ème approche utilise des fonctions virtuelles qui permettent de faire le calcul différemment en fonction de l'espèce.

    Mais pour utiliser une classe dans une hiérarchie de classe, il vaut mieux mettre le destructeur virtuel.
    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 Animal {
    protected :
    public :
        virtual ~Animal() = default;
        virtual int GetPoils(){return 0;}
    };
     
    Class Chat:public Animal{
      public: 
        int GetPoils() override{return UnCalculeMegaCompliquePourUnChat();}
    };
     
    Class Chien:public Animal{
      public: 
        int GetPoils() override{return UnCalculeMegaCompliquePourUnChien();}
    };
     
    class Un_type_sympa{
      public:
        int compte_les_poils_de(Animal a) {return a.GetPoils();};
    };
    A titre de curiosité, comment aurais-je fais pour déplacer le problème dans Un_type_sympa (comme initialement proposé)?
    Si le calcul est fonction de l'animal ET du type sympa, le problème n'est pas le même, il faut faire appel à des techniques de "double dispatch" comme de Design Pattern "Visitor".

    J'ai aussi une contrainte de temps d'execution (bcp d'itérations car beaucoup de paquets de poils), Est-ce que la résolution dynamique de méthode est plus efficiente?
    Ne jamais optimiser avant d'avoir un truc qui fonction. jamais.
    Une méthode virtuelle, c'est une indirection, qui est souvent "optimisée" via le cache.

    comment fait Un_Type_Sympa pour trouver le 'vrai' type du paramètre (cad la classe dérivée par la classe de base)?
    Si vous vous posez cette question, c'est que vous avez merdé votre conception.

    J'ai lu que l'utilisation de la vtable ralentit l'execution, comme j'ai une grosse contrainte de temps de calcul, une solution est-elle préférable plutôt qu'une autre?
    Merci !
    Si c'est l'utilisation d'une vtable qui vous ramenti, c'est qu'il faut complètement revoir votre conception car l'utilisation de "if" sur le type "réel" d'un objet est bien plus consommateur que l'indirection d'une vtable.

    J'ai lu que l'utilisation de la vtable ralentit l'execution, comme j'ai une grosse contrainte de temps de calcul, une solution est-elle préférable plutôt qu'une autre?
    Merci !
    Teste vraisemblablement fait avec des options de compilations "à la one again". En cas réel, c'est clairement moins, si cela à vraiment un impact négatif.
    Faites un truc qui fonctionne et qui est maintenable avant de faire des tests au doigt mouillé.

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

Discussions similaires

  1. Réponses: 1
    Dernier message: 25/11/2007, 12h28
  2. Problème avec une sous classe de Graphics2D
    Par Virgile le chat dans le forum 2D
    Réponses: 1
    Dernier message: 16/06/2007, 02h59
  3. Réponses: 1
    Dernier message: 17/11/2006, 16h57
  4. Instanciation d'une sous-classe
    Par iubito dans le forum Langage
    Réponses: 6
    Dernier message: 23/01/2006, 16h27

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