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 :

C++ : héritage et polymorphisme


Sujet :

C++

  1. #1
    Nouveau candidat au Club
    Homme Profil pro
    Ingénieur systèmes et réseaux
    Inscrit en
    Février 2013
    Messages
    1
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Ingénieur systèmes et réseaux
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Février 2013
    Messages : 1
    Par défaut C++ : héritage et polymorphisme
    Bonjour,

    Je débute en C++, et j'ai du mal à bien comprendre le fonctionnement de l'héritage et du polymorphisme. Dans mon code, je souhaite créer une classe "Garage" qui doit acceuillir des classes filles Moto, Voiture bâties sur la classe mère Vehicule. Pour cela, la classe "Garage" a un attribut de type "vector <Vehicule*>". Je veux écrire une méthode :
    "virtual void Garage::entree('référence à un objet de type Voiture ou Moto')" qui appelle le constructeur de l'objet correspondant, pour lui allouer un espace mémoire dans ma collection hétérogène de la classe "Garage". Voilà ce que j'ai, et qui est donc incorrect :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    void Garage::entree(Vehicule *vehicule)
    {
    	m_tableau.push_back(new Vehicule->vehicule());
    }
    Si vous pouviez prendre quelques minutes pour m'aider, merci.

    Bazorf

  2. #2
    Invité
    Invité(e)
    Par défaut
    Bonjour,

    Pourrions-nous avoir le code de tes classes véhicules ? Cela aiderait je pense.

  3. #3
    Inactif  


    Homme Profil pro
    Inscrit en
    Novembre 2008
    Messages
    5 288
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Secteur : Santé

    Informations forums :
    Inscription : Novembre 2008
    Messages : 5 288
    Par défaut
    Bonjour et bienvenue sur le forum

    Ton garage chez toi sait fabriquer des voitures ? Cool, tu me le prêtes ?

    C'est soit un problème de responsabilité (ton garage ne doit pas construire lui même les Véhicules et donc pas de new dans ta fonction), soit un problème de compréhension des pointeurs (avec new, tu dupliques l'objet pointé)

    Dans le second cas, je te conseille de lire un cours sur les pointeurs (ou encore mieux, d'oublier les pointeurs) et tester ça :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    void Garage::entree(Vehicule *vehicule)
    {
    	m_tableau.push_back(vehicule);
    }
    Si tu comprends pas la différence entre ton code et celui-là, je te conseille de relire une seconde fois le cours sur les pointeurs.

    Bon courage


    HS : Beaucoup de choses moches en 4 lignes de code Fais une recherche sur le forum sur le principe de responsabilité unique (SRP), sur les pointeurs intelligents (unique_ptr, shared_ptr), sur la conception objet en C++ en général. Ou sinon, tu attends que koala01 te fasse une réponse de 10 pages pour t'expliquer les problèmes de ton code (ou tu fais une recherche sur le forum "ce que koala01 n'aime pas dans votre code")

  4. #4
    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,
    A priori, tu transmet déjà un pointeur de type véhicule.

    Cela implique que tu as déjà créé "quelque part" un véhicule d'un type particulier (de type voiture ou de type moto).

    Il n'y aurait donc aucune raison pour devoir copier ce véhicule au moment où tu veux l'insérer dans le tableau.

    un simple
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    void Garage::entree(Vehicule *vehicule)
    {
    	m_tableau.push_back(vehicule());
    }
    devrait être amplement suffisant
    NOTA:Au passage, la fonction entree n'a, a priori, aucune raison d'être virtuelle ici, vu que de toute évidence, le comportement auquel elle correspond est strictement identique quel que soit le type de garage que tu envisages, et ce, même si tu envisage d'avoir un type de garage particulier pour chaque type de véhicule que tu auras à gérer

    La virtualité permet de mettre en place un comportement polymorphe, c'est à dire un comportement qui s'adapte au type réel de l'objet au départ duquel le comportement est appelé (ici, l'objet au départ duquel le comportement "entree" est appelé est... un garage ).

    Si un comportement n'a pas à être adapté au type réellement appelé, il ne sert à rien de rendre la fonction virtuelle, car cela représente quand même un cout que C++ nous permet d'éviter "à peu de frais" (et surtout, cela indiquera clairement qu'il ne faut pas redéfinir le comportement pour les classes dérivées, s'il y en a)

    De plus, tes classes Vehicule, Moto et Voiture ont toutes trois une sémantique d'entité.

    Cela implique qu'elle ne devraient pas être copiables.

    Eventuellement, tu peux envisager de les rendre clonables, ce dont je ne parlerai que sur demande (pour éviter d'écrire un roman trop long )

    Enfin,il existe ce que l'on appelle la loi demeter qui indique que si tu manipules un objet de type A (mettons, un garage) et que cet objet manipule en interne des objets de type B (mettons, des véhicules), tu ne devrais, a priori, absolument pas avoir à connaitre le type B (autrement dit les véhicules ) pour pouvoir manipuler ton objet de type A (autrement dit ton garage), à moins, bien sur, que la durée de vie des différents véhicules soit gérée "par ailleurs" (et de manière à ce qu'ils ne soient jamais détruits avant que le garage ne le soit lui-même ).

    L'idéal est donc que ce soit le garage qui se charge de demander la création des différents véhicules qu'il prend en charge

    D'un autre coté un des piliers de la programmation OO énoncé sous l'acronyme SOLID (le D mis pour DIP : Dependancies inversion principle, ou principe d'inversion des dépendances si tu préfères en français ) dit qu'aucun module de "haut niveau" ne devrait dépendre d'un module de "bas niveau":ils devraient dépendre d'abstraction, et qu'aucune abstraction ne devrait dépendre de détails: ce sont les détails qui devraient dépendre d'abstraction

    Du coup, ta classe garage ne devrait absolument rien connaitre de la classe voiture ou de la classe moto...

    Elle ne devrait les connaitre (et c'est ce qu'elle fait d'ailleurs ) que comme des véhicules.

    L'idéal, dans ce cas, est de déléguer la création des différents types de véhicules à une "fabrique de véhicules".

    La seule responsabilité de cette fabrique serait... de créer les différent types qui t'intéressent sur base des paramètres que tu auras fourni.

    Ce serait d'ailleurs, hormis une éventuelle classe visiteur (car il faudra sans doute en prévoir, vu qu'il faudra sans doute quand même faire la différence entre les voitures et les motos à un moment ou un autre) la seule classe qui devrait connaitre aussi bien les motos que les voitures

    Pour mettre ta fabrique au point, il y a en sommes deux solutions:

    Soit les paramètres nécessaires à la création d'une voiture sont différents en nombre ou en type de ceux qui sont nécessaires pour créer une moto, et tu peux donc fournir deux fonctions "créate" prenant différents paramètres et renvoyant un pointeur sur véhicule: la première renvoie une voiture créée sur base des paramètres qu'elle aura reçus, la deuxième renvoie elle une moto créée sur base des paramètres qu'elle aura reçus.

    Soit le nombre ou le type des arguments nécessaires à la création d'une moto sont les mêmes que ceux nécessaire à la création d'une voiture, et, dans ce cas, il faut prévoir une fonction avec un nom spécifique pour chaque type de véhicule (createCar et createMotorbyke ) qui renvoie le type correspondant sur base des paramètres reçus.

    En gros, cela ressemblerait donc à quelque chose comme
    VehicleFactory.hpp
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    class Vehicle; // ici, une déclaration anticipée suffit ;)
    class VehicleFactory
    {
        public:
            Vehicle * createCar(/* parametres */) const;
            Vehicle * createMotorbyke(/* paramètres */ ) const;
    };
    VehicleFactory.cpp
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    #include <VehicleFactory.hpp>
    #include <Car.hpp>
    #include <Motorbyke.hpp>
    Vehicle * VehicleFactory::createCar(/* parametres */) const
    {
        return new Car(/* paramètres*/);
    }
    Vehicle * VehicleFactory::createMotorbyke(/* paramètres */ ) const
    {
        return new Motorbyke(/* paramètres */);
    }
    De son coté, le garage devra juste savoir si l'entrée correspond à une moto ou à une voiture et s'occuper de récupérer les arguments qui vont bien, sous une forme 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
    25
    26
    27
    28
    29
    30
     
    void Garage::entree()
    {
        int choice;
        /* une implémentation "naïve" pour l'exemple ;) */
        std::cout << "Choisissez le type de vehicule:"<<std::endl
                 <<"\t-1 : moto"<<std::endl
                 <<"\t-2 : voiture"<<std::endl
                 <<"votre choix : "
        if(choice == 1)
        {
             entreeMoto();
        }
        else
        {
            entreeVoiture();
        }
    }
    void Garage::EntreeMoto()
    {
        /* tout ce qu'il faut pour avoir les paramètres nécessaires */
        VehicleFactory factory;
        m_tableau.push_back(factory.createMotorbyke(/* paramètres*/));
    }
    void Garage::EntreeVoiture()
    {
        /* tout ce qu'il faut pour avoir les paramètres nécessaires */
        VehicleFactory factory;
        m_tableau.push_back(factory.createCar(/* paramètres*/));
    }
    (Nota: il existe une foule de solution pour la fabrique dont certaines respectent encore mieux le DIP... mais j'ai fait au plus simple ici )
    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 Expert

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2007
    Messages
    1 895
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Septembre 2007
    Messages : 1 895
    Par défaut
    Je complète ce qui a été dit par @koala01 et @gbdivers - principalement pour participer à la sauvegarde des doigts de @koala01, qui, à force de taper du texte, doivent être usés jusqu'à l'os (non, il ne saigne pas ; il n'est pas tout à fait un homo sapiens sapiens de toute façon. Voyez le comme une espèce d'homo superior, avec un pouvoir du genre "peut écrire des romans sans saigner du bout des doigts". Je sais, comme pouvoir, c'est pas parfait, mais ça sert quand même !)

    Si tu souhaite réellement créer une copie de l'instance de véhicule que tu passes à la fonction, alors il existe un idiome simple à mettre en place : le clonage d'objets.

    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
     
    // dans la classe mère : 
    class Vehicule
    {
    public:
        // ...
        virtual Vehicle *clone() const = 0;
        // ...
    };
     
    // class dérivée : 
    class Moto
    {
    private:
        // pas une bonne idée de le mettre public
        Moto(const Moto& other); // copy ctor à implémenter
    public:
        // ...
        // on se sert du copy ctor, parce que ça rend le code bien lisible
        virtual Vehicule *clone() const { return new Moto(*this); }
        // ...
    };
    Du coup, ton code deviendrait :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    void Garage::entree(Vehicule *vehicule)
    {
    	m_tableau.push_back(vehicule->clone());
    }
    Ceci dit, tu ne souhaite pas faire ça. Au cas où ça ne soit pas clair : tu ne souhaites pas faire ça. Promis.

    Principalement parce que ça n'a pas de sens. Lorsque tu mets ta moto au garage, il est rare que tu y stocke une copie - non, c'est bien ta moto qui est placée dans le garage (sinon, tu viens de trouver une machine à prpduire de l'argent, et comme c'est moi qui te l'ai fait remarquer, un petit 15% de tes bénéfices ne serait pas volés ). Comme @koala01 te l'a fait remarquer, tes véhicules sont des entités - celles-ci ne sont pas censées exister en deux exemplaires (il n'existe pas de copie exacte de moi dans l'univers connu ou inconnu ; si elle existait, cette copie serait exactement à ma place, en train de taper ça - donc cette copie serait moi, hors elle ne peut pas être moi ET une copie (un être différent) de moi en même temps).
    [FAQ des forums][FAQ Développement 2D, 3D et Jeux][Si vous ne savez pas ou vous en êtes...]
    Essayez d'écrire clairement (c'est à dire avec des mots français complets). SMS est votre ennemi.
    Evitez les arguments inutiles - DirectMachin vs. OpenTruc ou G++ vs. Café. C'est dépassé tout ça.
    Et si vous êtes sages, vous aurez peut être vous aussi la chance de passer à la télé. Ou pas.

    Ce site contient un forum d'entraide gratuit. Il ne s'use que si l'on ne s'en sert pas.

  6. #6
    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 Emmanuel Deloget Voir le message
    principalement pour participer à la sauvegarde des doigts de @koala01, qui, à force de taper du texte, doivent être usés jusqu'à l'os (non, il ne saigne pas ; il n'est pas tout à fait un homo sapiens sapiens de toute façon. Voyez le comme une espèce d'homo superior, avec un pouvoir du genre "peut écrire des romans sans saigner du bout des doigts". Je sais, comme pouvoir, c'est pas parfait, mais ça sert quand même !)
    Fais gaffe, ne le répète pas trop souvent, sinon je risque de vous demander de m'appeler "maître" ou "dieu"
    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

Discussions similaires

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

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