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 surcharge


Sujet :

C++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre émérite Avatar de SofEvans
    Homme Profil pro
    Développeur C
    Inscrit en
    Mars 2009
    Messages
    1 084
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France

    Informations professionnelles :
    Activité : Développeur C

    Informations forums :
    Inscription : Mars 2009
    Messages : 1 084
    Par défaut Héritage et explication methode virtuelle
    Bonjour,

    Alors voila mon problème.

    Je créer une classe arme. Je fais toutes les fonctions membres et tout...
    Puis, je fais la fonction ChangerModeArme qui me permettra de selectionner entre "auto" et "single".
    Je me dit alors qu'un fusil sniper ou même un fusil a pompe n'ont pas de sélecteur.
    Je décide donc de créer (pour la première fois certes) un héritage.
    J'ai donc une classe mère ArmeModeFixe et une classe fille ArmeModeSelectif qui herite de la précédente avec la fonction membre ChangerModeArme.

    Mon problème vient ensuite.

    Je voudrais créer une sorte d'arsenal.
    Je créer un tableau contenant sur chaque ligne l'adresse d'une arme, peu importe quelle soit de type fixe ou sélectif.

    Or, si je créer ce tableau avec le type ArmeModeFixe, il me crache qu'il ne connait pas la fonction ChangerModeArme et si je créer le tableau avec le type ArmeModeSelectif, je ne peut pas y incorporer les occurrences de ArmeModeFixe.

    J'ai vraiment dû louper un chapitre mais je sais pas lequel

    Quelqu'un peut il me donner la methode afin de créer cette arsenal et afin de pouvoir utiliser toutes les fonctions des deux classe ?
    Faut-il faire une surcharge ?

    Merci de m'avoir lu.

  2. #2
    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,

    Déjà, il est difficile de soutenir qu'une arme à cadence de tir réglable soit une arme à cadence de tir fixe, parce que, justement, la cadence de tir est réglable pour le premier et non pour le deuxième

    Soutenir l'inverse est d'ailleurs tout aussi difficile pour la même raison

    Il ne me semble donc pas opportun de faire hériter les armes à cadence de réglable des armes à cadence de tir fixe

    Par contre, ce qui est beaucoup plus facile à soutenir, c'est le fait que les armes à cadence de tire réglables et les armes à cadence de tir fixe sont... des armes à feu (par opposition aux armes blanches, contondantes et autres )

    Et il pourrait très bien être de la responsabilité du type "armes à feu" de préciser si, oui ou non, la cadence de tire est réglable

    L'idéal serait donc une classe "ArmeAFeu", fournissant l'interface commune aux armes à feu des deux types ("ArmeACadenceReglable" et "ArmeACadenceFixe"), à savoir:
    • Une fonction permettant de connaitre le nombre de munition en magasin
    • Une fonction permettant de recharger le magasin
    • Une fonction permettant de tirer
    • Une fonction virtuelle (éventuellement pure) (méthode) permettant de savoir si l'arme est à cadence fixe
    • Une fonction virtuelle (éventuellement pure) (méthode) permettant de savoir si l'arme est à cadence réglable

    ArmeACadenceReglable et ArmeACadenceFixe héritant tous les deux de ArmeAFeu

    Cela pourrait prendre la forme 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
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    ArmeAFeu
    {
        public:
            ArmeAFeu();
            virtual ~ArmeAFeu();
            unsigned int ballesRestantes() const;
            void recharger();
            void tirer();
            virtual bool estACadenceFixe() const = 0;
            virtual bool estACadenceReglable() const = 0;
        private:
            /* les membres qui vont bien */
    };
    ArmeACadenceFixe : public ArmeAFeu
    {
        public:
            ArmeACadenceFixe();
            ~ArmeACadenceFixe();
            /* il faut redéfinir les deux méthodes virtuelles pures pour pouvoir
             * instancier cette classe
             */
            virtual bool estACadenceFixe() const ;
            virtual bool estACadenceReglable() const;
    }
    /*elles seront implémentées sous la forme de  */
    bool ArmeACadenceFixe::estACadenceFixe() const 
    {
        return true;
    }
    bool ArmeACadenceFixe::estACadenceReglable() const
    {
        return false;
    }
     
    ArmeACadenceReglable : public ArmeAFeu
    {
        public:
            ArmeACadenceReglable ();
            ~ArmeACadenceReglable ();
            /* il faut redéfinir les deux méthodes virtuelles pures pour pouvoir
             * instancier cette classe
             */
            virtual bool estACadenceFixe() const ;
            virtual bool estACadenceReglable() const;
            /* cette classe a une fonction particulière permettant de
             * sélectionner la cadence de tir
             */
            void changeCadence()
            {
                tirRapide ^= 1;
            }
            /* et un autre permettant de savoir si l'arme est réglée en tir rapide
             */
            bool enTirRapide() const{return tirRapide;}
        private:
            /* sans oublier le membre qui maintient cette information */
            bool tirRapide;
    }
    /*elles seront implémentées sous la forme de  */
    bool ArmeACadenceReglable::estACadenceFixe() const 
    {
        return false;
    }
    bool ArmeACadenceReglable::estACadenceReglable() const
    {
        return true;
    }
    Pour le reste, effectivement, lorsqu'un objet de type dérivé se fait passer pour un objet de type de base, on ne peut accéder qu'aux fonctions et méthodes... déclarées pour le type de base.

    Mais, maintenant, nous disposons de tout ce qu'il faut pour permettre à "ce qui va gérer nos armes" d'accepter (ou non) de changer la cadence de tir...

    L'interface graphique pourra par exemple déjà désactiver le bouton de sélection si on a affaire à une arme à cadence de tir fixe, sous une forme proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    if(perso.armeCourante()->estACadenceReglable())
        selecteur->activate();
    else
        selecteur->desactivate();
    et, quand il s'agira effectivement de sélectionner une cadence de tire, cela pourra se faire 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
     
    if(perso.armeCourente()->estACadenceReglable())
    {
        /* nous savons que nous avons affaire à une arme de type 
         * ArmeACadenceReglable... nous pouvons donc la convertir sans crainte
         */
        ArmeACadenceReglable* temp = static_cast<ArmeACadenceReglable*>
                                     (perso.armeCourente());
        temp->changeCadence();
    }
    Tu l'aura compris, la variable larme est un pointeur sur l'arme active que ton personnage a en main et la fonction perso.armeCouretente() renvoie un pointeur sur l'arme courante du perso

    Cette solution pourrait être améliorée, par exemple en utilisant le design pattern "decorateur" pour toutes les particularités similaires au fait de pouvoir sélectionner la cadence de tir
    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 émérite Avatar de SofEvans
    Homme Profil pro
    Développeur C
    Inscrit en
    Mars 2009
    Messages
    1 084
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France

    Informations professionnelles :
    Activité : Développeur C

    Informations forums :
    Inscription : Mars 2009
    Messages : 1 084
    Par défaut
    Oula, j'ai de gros progrès a faire en C++.

    Merci pour ta réponse (j'ai dû la relire deux fois avant de capter quelque chose), je vais potasser le lien que tu m'as filer.

  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
    Citation Envoyé par SofEvans Voir le message
    Oula, j'ai de gros progrès a faire en C++.

    Merci pour ta réponse (j'ai dû la relire deux fois avant de capter quelque chose), je vais potasser le lien que tu m'as filer.
    En fait, une grosse partie de ma réponse tient plus de la résolution de problèmes de conception que de problèmes propres au C++...

    Mais si elle éveille en toi des interrogations, n'hésite pas à nous en faire part, car la programmation a toujours été autre chose que de "vomir" des lignes de code
    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 émérite Avatar de SofEvans
    Homme Profil pro
    Développeur C
    Inscrit en
    Mars 2009
    Messages
    1 084
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France

    Informations professionnelles :
    Activité : Développeur C

    Informations forums :
    Inscription : Mars 2009
    Messages : 1 084
    Par défaut
    salut,

    Alors voila, j'ai commencé a lire les tutoriel en C++ mais comment dire ... malgré le fait qu'il soit très complet, ils sont aussi très compliquer pour mon niveau .
    Je bloque sur la notion de méthode virtuelle, méthode pure.
    Je comprend mal quand est-ce qu'on les emploi, pourquoi les emploi t'on a la place des méthode normal et quelles sont leur effets.
    Je sais que pour vous, la définition et les exemple du tuto sont aussi limpide que de l'eau de roche mais pour moi, c'est plutôt l'eau du port que je vois ....

    Bref, si une âme charitable voulais bien prendre le temps de m'expliquer tout ça, j'en serai très reconnaissant.

    Merci beaucoup

  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
    Nous allons donc commencer par le début...

    L'un des avantages apportés par le paradigme orienté objet est la possibilité de créer une relation forte entre deux types qui s'appelle "l'héritage".

    Cet héritage représente une relation "EST-UN" (is-a), au sens sémantique du terme.

    C'est le genre de relation qui pourrait exister entre une classe "chien" ou "chat" et une classe "animal" ou entre une classe "cercle", "triangle", "rectangle" ou "carré" et une classe "forme", parce que, sémantiquement, un chat ou un chien est un animal et un cercle, un triangle, un carré ou un rectangle sont des formes géométriques.

    L'idée est que, comme le cercle ou le rectangle (ou l'une des autres... formes pré-citées) sont des... formes géométriques, nous pouvons les faire passer pour... des formes géométriques, parce qu'il est possible de trouver un comportement "commun" (dans le sens du message recu ou envoyé par la classe) comme "tracer()"(pour les formes géométrique), "manger()", "dormir()"(pour les animaux)...

    Si tu souhaite généraliser ce fait, les classes cercle, rectangle, carré, triangle, chat et chien seront appelées "classe dérivées" parce qu'elles... dérivent d'une classe "mère", "parente" ou "de base" qui sont, dans les exemples, les classes "forme" et "animal".

    Le fait est que le chat ne dort ou ne mange pas forcément de la même manière que le chien, et encore moins de la même manière qu'une raie manta, qu'un dauphin ou qu'un éléphant, qui sont pourtant tous des animaux.

    Transposé aux formes géométriques, la manière de tracer un cercle est différente de celle à utiliser pour tracer un triangle, un rectangle ou un carré (et encore différente de celle de tracer un dodecagone, qui est pourtant aussi une forme géométrique )

    Quand on crée ces classes, il faut donc pouvoir prévenir le compilateur que
    Attention, ce qui sera effectué pour fournir le comportement dépendra du type réel de l'objet et peut ou doit être redéfini pour chaque type particulier
    La manière d'en prévenir le compilateur est de déclarer la fonction virtuelle (avec le mot clé virtual)

    Grace à ce mot clé, lorsque le compilateur sera en mesure de se dire "tiens, on me demande d'appeler la méthode (tracer(), manger() ou dormir()), mais je dois aller vérifier que cela ne représente pas un comportement propre au type réel de l'objet pour lequel on invoque ce comportement" (je te fais grâce des détails ardus de la manière dont il s'y prend )

    Mais nous sommes confrontés à un dernier problème (du moins, dans le cadre de ces explications) : Au niveau de la classe forme, nous n'avons strictement aucune information qui nous permettrait de tracer une forme géométrique

    Nous sommes donc dans l'impossibilité d'implémenter le comportement tracer() pour la classe forme (tout comme nous sommes dans l'impossibilité d'implémenter les différents comportements de la classe animal )

    Or, le compilateur a horreur du vide... Du coup, si nous ne disons rien de plus au compilateur (à l'éditeur de liens, en fait), il se plaindra de ne pas trouver l'implémentation (ce qui doit être fait par) de ces comportements...

    Il nous faut donc un moyen de signaler au compilateur quelque chose qui pourrait se dire sous une forme proche de
    Attention, je ne dispose pas des informations qui me permettraient de t'indiquer quoi faire, donc je ne le fais pas... Tu devra vérifier pour les types dérivés si je suis en mesure de te dire quoi faire.
    Ce moyen nous est donné par la possibilité de déclarer une fonction virtuelle pure, en rajoutant simplement un "=0" avant le point-virgule qui termine la déclaration de la fonction.

    Cela aura pour résultat de rendre la classe abstraite, c'est à dire une classe qui ne peut pas être instanciée par elle-même: on ne peut pas créer une instance (une variable du type) de cette classe directement.

    En effet, le compilateur a horreur du vide, hors, une classe qui contient ne serait-ce qu'une fonction virtuelle pure n'est... pas complète... Et il est programmé pour refuser de créer une variable de ce genre de classe.

    Par contre, une fois que toutes les fonctions virtuelles pures ont été redéfinies pour une classe dérivée, nous pouvons sans problème demander la création d'une variable du type dérivé et... la faire passer pour le type de la classe abstraite.

    Cela te parait il un peu plus clair, maintenant
    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

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

Discussions similaires

  1. Réponses: 6
    Dernier message: 08/02/2008, 14h58
  2. [Problème] Héritage et surcharge
    Par kamykaze dans le forum C++
    Réponses: 8
    Dernier message: 13/11/2007, 14h18
  3. [POO] Héritage et surcharge de méthodes
    Par defkid dans le forum Langage
    Réponses: 4
    Dernier message: 26/02/2007, 14h51
  4. héritage et surcharge
    Par babar63 dans le forum C++
    Réponses: 7
    Dernier message: 17/01/2007, 23h23
  5. [G++] Héritage et surcharge de fonction
    Par Beuss dans le forum Autres éditeurs
    Réponses: 11
    Dernier message: 15/05/2006, 09h18

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