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 :

polymorphisme ou non


Sujet :

C++

  1. #1
    Membre du Club
    Homme Profil pro
    Inscrit en
    Août 2013
    Messages
    274
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Août 2013
    Messages : 274
    Points : 56
    Points
    56
    Par défaut polymorphisme ou non
    Bonjour à tous,

    Je viens de voir un cours sur le polymorphisme, et je viens de faire dans un programme cette instruction suivante :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    void messageReceived( Message & msg)
    {
              BigMessage & bMessage = static_cast<BigMessage &> ( msg ); // <- polymorphisme ?
     
    }
    je voulais savoir si c'est du polymorphisme. Dans les cours que j'avais vu, le polymorphisme était de pouvoir lancer une méthode de la classe dérivé et non de la classe de base via le mot clé virtual meme si le type de l'objet était celui de la classe de base. Or la, il n'y a rien de cela, pourtant à partir de la classe de base Message, j'arrive a crée un objet BigMessage qui contient tous les élements de l'objet Message.

    Je viens de voir en cours l'héritage et le polymorphisme donc c'est un peu confus pour moi.
    Merci d'avance pour votre aide.

  2. #2
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 614
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Salut,

    Ca, ca n'a rien à voir avec le porlymorphisme... Au mieux, on pourrait dire que cela a à voir avec une utilisation pourrie de l'héritage.

    Le polymorphisme, en lui-même, c'est la capacité qu'a un objet (ou une fonction recevant un objet en paramètre) connu comme étant "du type de base" d'adapter son comportement au type réel de l'objet manipulé.

    Par exemple:
    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
    class Message{
    public:
        /* pour respecter la sémantique d'entité */
        Message(Message const & ) = delete;
        Message& operator = (Message const &) = delete;
        virtual ~Message() = default;
        /* une fonction polymorphe, rendue "virtuelle pure" parce que l'on ne dispose pas  
         * des informations nécessaires pour afficher un message dont ... on ne sait rien
         */
        virtual void printMe() const =0 ;
    };
    /* une forme bien particulière de message : les salutations */
    class Greatings : public Message!
    public:
        /* l'adaptation du comportement */
        void printMe() const override{
            std::cout<<"Hello \n";
        }
    };
    /* une autre forme de message : les au revoirs */
    class GoodBye : public Message{
    public:
        /* idem */
        void printMe() const overrride{
            std::cout<<"Good bye\n";
        }
    };
     
     
    /* une fonction libre utilisant le polymorphisme 
     * elle accepte n'importe quel type de message
     */
    void foo(Message const & m){
        m.printMe(); // appel du comportement polymorphe
    }
    int main(){
        Greatings hello;
        GoodBye goodbye;
        foo(hello); // prints "Hello"
        foo(goodbye); // prints "Good bye"
    }
    on peut aussi travailler directement avec les objets, sans passer par une fonction "intermédiaire":
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    int main(){
        /* une collection de messages, dont on ignore tout */
        std::vector<std::unique_ptr<Message>> messages;
        /* on y ajoute des salutations */
        messages.push_back(std::move(std::make_unique<Greatings>()));
        /* et des au revoirs */
        messages.push_back(std::move(std::make_unique<GoodBye>()));
        /* une fois placés dans la collections, il sont tous connus comme "des messages",
         * si j'avais fait créer les deux messages "ailleurs", tu n'auras pas connu leur type réel 
         */
        for(auto const & it : messages){ // on parcourt tous les messages, sans savoir ce qu'ils sont en réalité
            it.get()->printMe(); // mais eux, ils le savent très bien
        }
    }
    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

  3. #3
    Membre du Club
    Homme Profil pro
    Inscrit en
    Août 2013
    Messages
    274
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Août 2013
    Messages : 274
    Points : 56
    Points
    56
    Par défaut
    merci pour ta réponse.

    l'instruction : BigMessage & bMessage = static_cast<BigMessage &> ( msg );

    est déguelasse, source de probleme ou ca fait partie des choses que l'on peut faire avec de l'héritage ?

  4. #4
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 614
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    L'instruction BigMessage & bMessage = static_cast<BigMessage &> ( msg ); est pourrave car source d'énormément de problèmes :

    Primo, elle utilise static_cast, ce qui fait que c'est un transtypage qui se base sur la connaissance que le compilateur a de la hiérarchie des classes en présence.
    Mettons que nous ayons une hiérarchie de classes proche de
    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
    class MessageBase{
        /* je passe les détails */
    };
    class ShortMessage: public MessageBase{
     
    };
    class BigMessage : public MessageBase{
     
    };
    class CoolMessage: public MessageBase{
     
    };
    class HotMessage : public MessageBase{
     
    };
    Pour peu que le compilateur ait connaissance de BigMessage (vu que c'est en cela que l'on va essayer de transtyper msg), le compilateur ne verrait aucune raison à refuser le transtypage tant que msg sera connu comme une référence (ou un pointeur) sur un objet de type MessageBase; y compris si mgs est en réalité du type ShortMessage, CoolMessage ou HotMessage.

    Il y a une raison toute simple à cela : la vérification s'effectue de manière statique (comme le nom static_cast l'indique si bien ), c'est à dire, au moment de la compilation, bien avant qu'il ne soit question d'avoir effectivement créé un objet du type indiqué lors de l'exécution.

    Comme BigMessage hérite de MessageBase, le compilateur acceptera le transtypage, mais cela revient -- pour toutes les autres classe dérivées de MessageBase à mentir au compilateur en lui demandant de considérer bMessage comme un objet d'un type ... qu'il n'est absolument pas

    Comme la taille nécessaire à la représentation en mémoire de ces différentes classes est susceptible d'être différente et que les fonctions auxquelles on accède du fait du transtypage (comprend : celles qui n'existent, dans l'accessibilité publique, qu'au niveau d'une classe dérivée bien particulière) sont -- a priori -- spécifiques à la classe en question (BigMessage, en l'occurrence), tu imagines un peu le bordel que cela pourrait occasionner si on essayait d'appeler une fonction spécifique à BigMessage alors que msg est en réalité de type HotMessage

    A moins d'être sur que l'on a effectivement affaire à un BigMessage (mais, alors, pourquoi ne pas l'avoir transmis en tant que tel (*) ) un transtypage dont la validité ne pourrait être vérifiée qu'à la compilation risque de nous envoyer directement "dans les choux"

    L'alternative, si l'on n'a aucun moyen de garantir le type de msg, serait sans doute d'utiliser dynamic_cast avec un pointeur, par exemple:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    BigMessage * bptr = dynamyc_cast<BigMessage * >(&msg); // on considère que msg a été transmis par référence ...
    if(bptr){
         // message est bel et bien de type BigMessage
    }
    Mais cela ne résoudrait qu'une partie du problème, car, avec la multiplication des classes dérivées, nous finirons par nous retrouver avec un code proche de
    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
    /* je reprends la hiérarchie de
     * mon exemple : MessageBase comme classe de base, les classes
     * - ShortMessage
     * - BigMessage
     * - CoolMessage
     * - HotMessage
     * comme classes dérivées
     */
    ShortMessage * sptr = dynamic_cast<ShortMessage * >(&msg); // == nullptr si msg n'est pas du type ShortMessage
    if(sptr){ // non null si msg est de type ShortMessage
        /* on peut l'utiliser comme un ShortMessage */
    }
    BigMessage * bptr = dynamic_cast<BigMessage * >(&msg); // == nullptr si msg n'est pas du type BigMessage 
    if(bptr){ // non null si msg est de type BigMessage 
        /* on peut l'utiliser comme un BigMessage  */
    }
    CoolMessage* cptr = dynamic_cast<CoolMessage* >(&msg); // == nullptr si msg n'est pas du type CoolMessage
    if(sptr){ // non null si msg est de type CoolMessage
        /* on peut l'utiliser comme un CoolMessage */
    }
    HotMessage* hptr = dynamic_cast<HotMessage* >(&msg); // == nullptr si msg n'est pas du type HotMessage
    if(hptr ){ // non null si msg est de type HotMessage
        /* on peut l'utiliser comme un HotMessage */
    }
    Avoue que cela a l'air génial ce genre de code, non En plus, avec un peu d'imagination, nous pourrions même envisager d'ajouter une énumération représentant les différents types, et l'utiliser pour déterminer en quel type nous devons essayer de transtyper msg !

    Si ce n'est que, sur l'ensemble de ton projet, combien de fois vas tu avoir recours à un tel code Quinze fois (sur un projet moyen) Cent fois (sur un projet à peine plus gros) 3552 fois (sur un projet "un peu conséquent")

    Alors, mettons que dans trois mois, six mois ou un an (ou plus, qui sait ), tu décide de rajouter la classe
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    class PressuredMessage : public MessageBase{
        /* on passe encore les détails */
    };
    Tu devras modifier ce genre de code partout où il existe, avec, comme sanction immédiate (même si les effets risquent de se faire attendre) que chaque endroit où tu auras oublié d'ajouter la possibilité de convertir msg en PressuredMessage provoquera un bug

    Et plus le temps se sera écoulé entre l'écriture d'un tel code et le moment où il faudra ajouter "une nouvelle classe" à prendre en ligne de compte augmentra, plus tu risquera d'oublier de modier certaines parties du code. Tu te souviendras sans doute des endroits du code créés la veille ou, dans le meilleur des cas, trois jours plus tôt. Mais rarement mieux.

    En bref, un tel code ne respecte pas le deuxième principe SOLID : le O mis pour OCP (Open Closed Principle, ou principe Ouvert / Fermé si tu préfères en francais) : un code doit être "ouvert aux évolutions" (il doit rester "simple" d'ajouter une nouvelle classe) mais "fermé au modifications" (il ne faut pas que l'on doive modifier du code existant et testé pour pouvoir ajouter une nouvelle fonctionnalité). Et ca, c'est une source inépuisable d'emmerdes
    (*) Bon, pour être honnête, il y a des cas bien particuliers dans lesquels nous n'aurons sans doute pas d'autres choix que d'avoir recours au transtypage, mais, de manière générale, le double dispatch s'avèrera bien plus intéressant
    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

  5. #5
    Membre du Club
    Homme Profil pro
    Inscrit en
    Août 2013
    Messages
    274
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Août 2013
    Messages : 274
    Points : 56
    Points
    56
    Par défaut
    merci beaucoup pour ta réponse détaillée

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

Discussions similaires

  1. [Résolu] Polymorphisme non autorisé par Eclipse
    Par ilansas dans le forum Android
    Réponses: 2
    Dernier message: 05/05/2014, 16h17
  2. Polymorphisme et pile non exécutable
    Par Sat80 dans le forum x86 32-bits / 64-bits
    Réponses: 11
    Dernier message: 24/09/2010, 19h39
  3. Réponses: 3
    Dernier message: 24/04/2010, 12h29
  4. Polymorphisme : non-reconnaissance de méthode
    Par Chen norris dans le forum C++
    Réponses: 4
    Dernier message: 24/08/2006, 11h52
  5. Réponses: 6
    Dernier message: 21/06/2002, 14h48

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