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 :

Constructeur de classe differente qui s'imbrique sans le vouloir


Sujet :

C++

  1. #1
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Novembre 2005
    Messages
    93
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Novembre 2005
    Messages : 93
    Points : 36
    Points
    36
    Par défaut [Résolu] Constructeur de classe differente qui s'imbrique sans le vouloir
    Je débute dans l'apprentissage du C++ en suivant notamment les cours et tutoriels C++.
    Ils donnent l'exemple pour apprendre a utiliser les classes un trés simple rpg à 2 joueurs.

    Pour m'amuser un peu et pour m'entrainer j'ai un peu modifié le code mais j'ai un souci.

    Je m'explique:

    J'ai une classe personnage qui me permet de gerer mes joueurs. Le constructeur de cette classe lance deux fonctions pour donner un nom et choisir une arme.

    J'ai une autre classe Combat qui me sert à gerer le combat (tour du joueur, etc.) et son constructeur n'est qu'une liste d'initialisation.

    Dans mon main je crée deux objets combat et joueur1 et joueur2 et JUSTE ca. Voyez:

    main.ccp :
    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
     
    #include <iostream>
    #include <string>
     
    #include "Personnage.h"
    #include "Combat.h"
     
    using namespace std;
     
     
    int main()
    {
        // On démarre le jeu
        Combat combat1;
     
        // On créé les deux personnages joueur1 et joueur2
        Personnage joueur1, joueur2;
        cout << "lol" << endl;
     
        return 0;
    Personnage.cpp:
    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
     
    #include <iostream>
    #include <string>
    #include <limits>
    #include "Personnage.h"
    #include "Arme.h"
     
    using namespace std;
     
    // Constructeur
    Personnage::Personnage() : m_vie(100), m_mana(100), m_nbPotion(3)
    {
        donnerNom();
        donnerArme();
    }
     
    // Methodes
     
    // Définir le nom et l'arme du personnage
    void Personnage::donnerNom()
    {
        cout << "Quel est le nom du joueur ?" << endl;
        cin >> m_nom;
    }
     
     
    void Personnage::donnerArme()
    {
        int choixArme;
     
        cout << "Quel arme choisissez-vous ?" << endl;
        cout << "1 - La lance (dégats : 18 pts)" << endl;
        cout << "2 - L'epée en bronze (dégats : 19 pts)" << endl;
        cout << "3 - La hache de fer (dégats : 20 pts)" << endl;
        cout << "Votre choix : ";
        cin >> choixArme;
    }
    Personnage.h :
    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
     
    #ifndef DEF_PERSONNAGE
    #define DEF_PERSONNAGE
     
    #include <string>
    #include "Arme.h"
     
    class Personnage
    {
        public:
     
        // Constructeur
        Personnage();
        // Methodes
        // Definir le personnage : Nom + Arme
        void donnerNom();
        void donnerArme();
     
        private:
     
        std::string m_nom;
        int m_vie;
        int m_mana;
        int m_nbPotion;
        Arme m_arme;
     
    };
     
    #endif
    et enfin combat.cpp :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
     
    #include <iostream>
    #include <limits>
    #include "Combat.h"
    #include "Personnage.h"
     
    using namespace std;
     
    //Constructeur
    Combat::Combat()
    {
     
    }
    Oups j'avais oublié le combat.h
    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
     
    #ifndef DEF_COMBAT
    #define DEF_COMBAT
     
    #include "Personnage.h"
     
    class Combat
    {
        public:
     
        // Constructeur
        Combat();
     
    };
     
     
    #endif

    Le probléme est que bien que le main doit crée deux objets joueur1 et joueur2 , quand j'execute mon programme la création d'un joueur (nom + arme) s'effectue 3 fois. En essayant de trouve le probléme, je me suis rendu compte que la ligne Combat combat1 lancé le constructeur de la classe Combat mais ce dernier lancé tout seul le constructeur de la classe Personnage.

    Je reste bloqué sur cette erreur sans comprendre ce que j'ai mal codé. Merci de votre aide.

  2. #2
    Membre chevronné
    Avatar de Goten
    Profil pro
    Inscrit en
    Juillet 2008
    Messages
    1 580
    Détails du profil
    Informations personnelles :
    Âge : 33
    Localisation : France

    Informations forums :
    Inscription : Juillet 2008
    Messages : 1 580
    Points : 2 205
    Points
    2 205
    Par défaut
    on pourrait voir combat.h ?
    "Hardcoded types are to generic code what magic constants are to regular code." --A. Alexandrescu

  3. #3
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Novembre 2005
    Messages
    93
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Novembre 2005
    Messages : 93
    Points : 36
    Points
    36
    Par défaut
    Citation Envoyé par Goten Voir le message
    on pourrait voir combat.h ?
    C'est corrigé.

    Désolé, je repeterais cent fois "tu reliras ton message avant de poster"

  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
    Salut,

    L'idéal serait de demander avant la construction de ton personnage quelle est l'arme que tu veux lui donner, de construire cette arme (qui ferait partie d'une hiérarchie de classe de type... Arme) et de donner à ton joueur l'arme demandée.

    tu en arriverais donc à quelque chose 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
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    /* déclaration anticipée de Joueur... du fait des références croisées */
    class Joueur;
    /* déclaration anticipé de Amelioration... du fait des références croisées */
    class Amelioration;
    /* Définissons ce que l'on appelle une "interface" ou une classe
     * abstraite, qui présente les fonctions communes à toutes les armes
     */
    class Arme
    {
        private:
             /* Une arme appartient au joueur qui l'utilise, nous
              * devons pouvoir y faire référence
              */
            Joueur * m_user;
            /* Une arme produit une certaine valeur "de base" de dégats 
             * une valeur négative représente un regain de santé
             */
           int m_degat; 
       public:
          /* Le constructeur de l'arme... par défaut, elle n'aparatient à
           * personne mais nous indiquons les dégats de base
           */
          Arme(int d):m_degat(d),m_user(0){}
          /* le destructeur, virtuel pur, car on ne peut pas instancier
           * une arme sans savoir de quel type elle est
           */
          virtual ~Arme() = 0;
          /* Nous pouvons définir qui tient l'arme en main et récupérer
           * le joueur qui la tient
           */
          void setUser(Joueur* j){m_user = j;}
          Joueur * const user() const{return m_user; }
          /* une arme sert, essentiellement, à attaquer un joueur...
           */
        void attaquer(Joueur* ) const ;
          /* Elle peut aussi servir à parer un coup */
          virtual int parer(int);
          /* Elle peut enfin éventuellement recevoir des améliorations */
          virtual bool ameliorer(Amelioration * );
          /* enfin, nous pouvons savoir quels dégats l'arme fait en tout */
         virtual int degats() const;
    };
    Ces différents comportements peuvent être définis pour une arme "de base" sous 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
    void Arme::attaquer(Joueur * cible) const
    {
         int degatReels=degats();
        /* on calcule les dégats réels à l'aide des capacités du joueur
         * qui tient l'arme
         * (non implémenté ici) puis on attaque le joueur cible avec cette
         * valeur de dégats
         */
       cible->subitAttaque(degatsReels);
    }
    int Arme::parer(int degatbase)
    {
        /* L'arme de base n'a aucune valeur de parade... Celui qui la 
         * tient en main subit l'intégralité des dégats
         */
        return degatbase;
    }
    bool Arme:ameliorer(Amelioration *)
    {
        /* une arme de base ne peut subir aucune amélioration */
        return false;
    }
    /* une arme par défaut ne produits que ses propres dégats */
    int Arme::degats()
    {
        return m_degats;
    }
    Nous pouvons ensuite décider de créer différentes armes (épées, lances, haches, ...)

    Nous pouvons considérer que toute arme tranchante "de base" peut subir une (et une seule) amélioration: l'aiguisage
    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
    class EpeeBronze : public Arme
    {
        private:
            Amelioration * m_prem;
            int m_parade;
        public:
            /* Par défaut, l'épée de bronze n'est pas aiguisée, inflige 19 
             * points de dégats et permet d'éviter 4 points de dégats sur le total
             * (par exemple)
             */
            EpeeBronze():Arme(19),m_prem(0),m_par(4){}
            virtual ~EpeeBronze();
            /* Elle peut subir une et une seule amélioration (l'aiguisage) 
             * Nous redéfinissons donc la méthode ameliorer
             */
            virtual bool ameliorer(Amelioration *);
            /* et nous adaptons le comportement le calcule des dégats 
             * à cette qualité supplémentaire
             */
            virtual int degats() const;
            /* et nous redéfinissons le comportement en cas de parade */
            virtual int parer(int);    
     
    };
    qui sera implémentée 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
     
    /* l'aiguisage donne un bonus de 4 points de dégats */
    int EpeeBronze::degat() const
    {
        if(m_prem)
            return 4+Arme::degat();
        return Arme::degat();
    };
    int EpeeBronze::parer(int degatbase)
    {
        return degatbase -m_parade;
    }
    bool EpeeBronze:ameliorer(Amelioration * am)
    {
        /* si elle est déjà améliorée, on ne peut pas le faire une seconde fois
         * si on essaye de l'améliorer autrement qu'en l'aiguisant,
         * cela ne fonctionne pas
         */
        if(m_prem)
            return false;
        if(dynamic_cast<Aiguisage*>(am)==0)
            return false;
        m_prem=am;
        return true;
    }
    /* quand on détruit l'épée, on détruit son amélioration */
    EpeeBronze::~EpeeBronze(){delete m_prem;}
    (Tu peux, si tu le souhaite, créer une épée de bronze magique qui hérite de EpeeBronze )

    Je ne remet pas le code pour les lances et les haches, mais il sera basé sur le même principe

    Un sors peut être considéré comme un arme, à ceci près qu'il va en plus demander au joueur de diminuer ses points de mana...

    Le plus marrant de l'histoire étant qu'un sort de soins attaquera... le joueur qui le tient et lui infligera des dégats négatifs

    Ta classe Joueur ressemblerait à
    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
    #include Arme.h"
    class Joueur
    {
        private:
            /* un joueur dispose à la base d'une arme, d'une quantité de vie
             * et d'une quantité de mana
             * il est en outre dans un état d'attaque ou de défence 
             */
           Arme * m_weapon;
           unsigned int m_live;
           unsigned int m_mana;
           bool m_state; /* true == attaque, false == defence */
        public:
            Joueur(Arme* myarme):m_arme(myarme),m_live(100),m_mana(80)
            m_state(true)
            {
                 /* on définit le joueur comme portant l'arme */
                 m_arme->setUser(this);
            }
            ~Joueur(){delete m_arme}
            /* on peut changer d'arme */
            void changeArme(Arme* newweapon)
            {
                /* voir si on détruit l'ancienne ou si on la met dans l'inventaire 
                 * ici on la détruit
                 */
                delete m_weapon;
                m_weapon = newweapon;
            }
            /* on peut se soigner */
           void heal()
           {
                m_mana-= 20;
                m_live += 18;
            }
            /* on peut attaquer un joueur */
            void attack(Joueur cible)
            {
                 m_weapon->attaquer(cible);
            }
            /* on peut changer d'état et savoir dans quel état on se trouve */
            void stateAttack(){m_state = true;}
            void stateDefend(){m_state = false;}
            bool state() const{return m_state;}
            /* Et enfin se faire attaquer */
            void subitAttaque(int degats)
            {
                /* on calcule les dégats réellement subit sur base de
                 * différents critère (état, armure, résistances diverses)
                 * et on les déduits de notre vie
                 */
                m_live-=degats;
            }
    };
    Le tout étant utilisé 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
    31
    32
    33
    /* j'ai pas envie de répéter deux fois le code pour les deux joueurs */
    Arme* choisirArme(int numJoueur)
    {
        int choixArme;
     
        cout << "Quel arme choisissez-vous pour le joueur "
                << numJoueur<< " ?" << end
                << "1 - La lance (dégats : 18 pts)" << endl
                << "2 - L'epée en bronze (dégats : 19 pts)" << endl
                << "3 - La hache de fer (dégats : 20 pts)" <<  endl
               << "Votre choix : ";
        cin >> choixArme;
        if(choixArme==1)
            return new Lance;
        if(choixArme==2)
            return new EpeeBronze;
        if(choixArme==3) 
            return new HacheFer;
        return NULL;
    }
    int main()
    {
        Arme * first = choisirArme(1);
        Arme * second = choisirArme(2);
        Joueur J1(first);
        Joueur J2(second);
        /* on peut maintenant jouer */
        J1.stateAttack();
        J2.stateDefend();
        J1.attac(&J2);
        J1.stateDefend();
        /*...*/
    }
    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
    Invité
    Invité(e)
    Par défaut
    Citation Envoyé par beware Voir le message
    En essayant de trouve le probléme, je me suis rendu compte que la ligne Combat combat1 lancé le constructeur de la classe Combat mais ce dernier lancé tout seul le constructeur de la classe Personnage.
    La raison la plus probable serait que ta classe combat contienne un membre de la classe Personnage, ou d'une classe ayant un Personnage pour membre.

    Par exemple, si ta classe contient, comme membre, le nom du vainqueur du Combat, un Personnage fictif sera créé à la création de chaque Combat.

    Pour éviter cela, il faudrait remplacer le membre par un pointeur sur le Personnage vainqueur, ou quelque chose du genre.

    Maintenant, mettre du code d'interface dans un constructeur de classe (ici la saisie du nom et de l'arme) n'est généralement pas une bonne idée. Tu risques, dans ton application, de souvent créer des Personnages temporaires, pour stocker des calculs intermédiaires par exemple, et ce code d'interface va revenir te mordre...

    Francois

  6. #6
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Novembre 2005
    Messages
    93
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Novembre 2005
    Messages : 93
    Points : 36
    Points
    36
    Par défaut
    Citation Envoyé par koala01 Voir le message
    Salut,

    L'idéal serait de demander avant la construction de ton personnage quelle est l'arme que tu veux lui donner, de construire cette arme (qui ferait partie d'une hiérarchie de classe de type... Arme) et de donner à ton joueur l'arme demandée.
    Je ne doute pas que ton code soit bien meilleur et plus complet que le mien, mais il y a plein de chose que je comprends pas à cet instant.

    Je n'en suis qu'au début de mon apprentissage.

    Mais merci quand même cela m'a donnée quelques idées.


    Citation Envoyé par fcharton Voir le message
    La raison la plus probable serait que ta classe combat contienne un membre de la classe Personnage, ou d'une classe ayant un Personnage pour membre.

    Par exemple, si ta classe contient, comme membre, le nom du vainqueur du Combat, un Personnage fictif sera créé à la création de chaque Combat.

    Pour éviter cela, il faudrait remplacer le membre par un pointeur sur le Personnage vainqueur, ou quelque chose du genre.

    Maintenant, mettre du code d'interface dans un constructeur de classe (ici la saisie du nom et de l'arme) n'est généralement pas une bonne idée. Tu risques, dans ton application, de souvent créer des Personnages temporaires, pour stocker des calculs intermédiaires par exemple, et ce code d'interface va revenir te mordre...

    Francois
    Alors, pour commencer, effectivement, il y a un membre de la classe Personnage dans la classe Combat (il s'agit du nom du joueur).

    Donc il ne me reste plus qu'a continuer le cours pour trouver une solution.

    Merci pour ta réponse.
    Merci également pour ton conseil de ne pas mettre d'interface dans un constructeur. Je vais changer mon code en ce sens.


    EDIT : Le probléme semble avoir disparu rien qu'en enlevant l'interface dans le constructeur.

  7. #7
    Membre chevronné
    Avatar de Goten
    Profil pro
    Inscrit en
    Juillet 2008
    Messages
    1 580
    Détails du profil
    Informations personnelles :
    Âge : 33
    Localisation : France

    Informations forums :
    Inscription : Juillet 2008
    Messages : 1 580
    Points : 2 205
    Points
    2 205
    Par défaut
    Alors, pour commencer, effectivement, il y a un membre de la classe Personnage dans la classe Combat (il s'agit du nom du joueur).
    Alors c'est normal... Quand tu crées un objet de type Combat, un objet de type Personnage sera construit. Que tu t'en serves ou non d'ailleurs.
    "Hardcoded types are to generic code what magic constants are to regular code." --A. Alexandrescu

  8. #8
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Novembre 2005
    Messages
    93
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Novembre 2005
    Messages : 93
    Points : 36
    Points
    36
    Par défaut
    Citation Envoyé par Goten Voir le message
    Alors c'est normal... Quand tu crées un objet de type Combat, un objet de type Personnage sera construit. Que tu t'en serves ou non d'ailleurs.
    ET dans l'absolu, c'est une géne ou pas?

  9. #9
    Membre chevronné
    Avatar de Goten
    Profil pro
    Inscrit en
    Juillet 2008
    Messages
    1 580
    Détails du profil
    Informations personnelles :
    Âge : 33
    Localisation : France

    Informations forums :
    Inscription : Juillet 2008
    Messages : 1 580
    Points : 2 205
    Points
    2 205
    Par défaut
    Bas non, dans l'absolu si t'as besoin d'un membre alors t'en as besoin... donc tu l'inclus dans ta classe. Pour pouvoir s'en servir faut qu'il soit construit, ce qui est fait quand tu construits ta classe. Donc pas de soucis.
    "Hardcoded types are to generic code what magic constants are to regular code." --A. Alexandrescu

  10. #10
    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
    Citation Envoyé par beware Voir le message
    Je ne doute pas que ton code soit bien meilleur et plus complet que le mien, mais il y a plein de chose que je comprends pas à cet instant.

    Je n'en suis qu'au début de mon apprentissage.

    Mais merci quand même cela m'a donnée quelques idées.
    Là est peut être tout le problème... (soit dit sans vouloir t'offencer)

    Tout à "l'excitation" d'apprendre un langage de programmation, j'ai l'impression que tu te lances dans un projet pour lequel tu n'as qu'une idée bien trop imprécise de ce que tu veux, et de la manière de l'obtenir, alors qu'il te manque des concepts "de bases".

    Il n'est donc pas impossible que, pour un premier projet, tu ne sois occupée à viser "un peu trop haut" par rapport à tes connaissances .

    Comprenons nous bien, le but de ces écrit n'est pas de te décourager de mener ton projet à bien, le but est de te faire prendre conscience que, si tu démarres un projet en n'ayant pas les bases suffisantes, tu risques très fort soit d'être bloquée en cours de route, soit, parce que (c'est du moins tout le mal que je te souhaites) tes connaissances auront évolué et que tu en sera réduite, pour faire évoluer ton projet, à "tout casser"... Ce qui est particulièrement agaçant

    Contrairement à ce que l'on croit (mais c'est une erreur que tout programmeur débutant a faite au moins une fois) la programmation ce n'est pas se "ruer" sur le clavier et commencer à écrire du code, en espérant que ca marche (ou même que ca fasse ce que l'on attend de lui)...

    En effet, l'expérience montre que les besoins que doit rencontrer ton projet évoluent en (quasi) permanence.

    Si tu ne tiens pas suffisamment compte de l'évolution future de ton projet, il arrivera rapidement un moment où tu ne pourra plus le faire évoluer sans "tout casser".

    Et la question n'est pas de savoir si cela va arriver, mais quand cela arrivera

    En fait, une fois que l'on a "l'idée de base" d'un projet, il y a quatre grandes étapes à suivre (et tu remarquera rapidement qu'il faut régulièrement revenir sur plusieurs de ces étapes):
    1. Exprimer les besoins "ressentis" qui sous-tendent à ton idée de base
    2. Analyser ces besoins pour déterminer quels types de données permettront de les rejoindre
    3. Concevoir la manière dont les différents types seront mis en oeuvre et réagiront en vue de satisfaire les besoins
    4. Implémenter les différents types et les fonctions qui vont bien pour obtenir le résultat souhaité
    Evidemment, il est possible à tout moment de se rendre compte qu'il faut rajouter un besoin à satisfaire, ce qui nécessite de recommencer le processus en essayant de garder le maximum de l'existant.

    Dans ton cas, tu as déjà "épinglé" trois acteurs important:
    • Le combat
    • Le joueur
    • Les armes
    Il est donc important de s'intéresser au "kifékoi" afin que chaque acteur ait une responsabilité qui lui est propre.

    Le combat va réunir... deux (ou plusieurs peut être dans les évolutions) combattants.

    C'est donc lui qui doit s'occuper de gérer les combattants, mais, cela implique que sa responsabilité est aussi celle de... donner l'arme au(x) combattant(s)

    C'est donc à lui que revient la responsabilité de demander à chaque joueur quelle arme il choisi pour le combattant qui le représente

    Le combattant, quant à lui, utilise une arme et sa responsabilité est... d'attaquer l'autre (les autres) combattant(s).

    Il dispose également d'autres caractéristiques particulières (ses points de vie et de mana, principalement, parce que tu les a déjà représentées), et de comportements qui lui permettent de gérer ces caractéristiques

    Enfin, l'arme dispose d'une caractéristique représentant les dégâts qu'elle peut infliger, et sa responsabilité principale est... d'infliger ces dégâts... au combattant attaqué (ce qui revient à dire au combattant attaqué qu'il vient de subir XXX points de dégats ).

    Par la suite, il n'est pas impossible que tu décide qu'une partie se fait en trois manches (trois combats), que tu décides de garder "les meilleurs scores", que tu veuille rajouter de la musique, voire, des graphismes etc...

    Mais ces aspects devront donc être pris en charge (si tu décide de les ajouter à ton projet) par... une ou plusieurs classes qui s'occupera(ront) de gérer le combat

    Tu remarquera que j'ai "naturellement" commencer à parler de combattant et non de joueur...

    C'est parce que le joueur est ce qui... manipule le personnage qui combat

    Il n'est en effet pas impossible que, plus tard, tu décide de permettre à un combattant géré par... un joueur "humain" de combattre contre un combattant géré par... l'intelligence artificelle (l'ordinateur )

    Cela nous ouvre la voie royale pour faire la distinction entre les deux: les PJ (Personnages Joueurs) et les PNJ (Personnages Non Joueur, NPC en anglais)

    De plus, si tous les personnages joueurs sont (surement) des combattants, il n'est pas impossible que tu décide que certains PNJ ne le soient pas, mais qu'il soient là soit pour faire "simplement" partie du décors, soit, pour aider le personnage joueur (en lui donnant des quêtes, en lui vendant des pieces d'équipement, ou en lui rachetant ce que le personnage joueur a trouvé)...

    Si tu en arrives à cette situation, il est "logique" que tu décide de considérer, que les armes sont... des pièces d'équipement un peu particulières (dans le sens où elles servent à... attaquer)

    Et tu décidera sans doute que d'autres pièces d'équipements permettent de modifier les caractéristiques du joueur

    Du coup, tu ne pourra plus *décemment* estimer que les dégats infligés par une arme ne dépendent que de l'arme utilisée (parce que la force du personnage qui la tient, ou la connaissance que le personnage qui la tient a du maniement de l'arme influeront énormément sur les dégâts réellement infligés)

    Dans le même ordre d'idée, comme tu as prévu trois armes différentes, il n'est pas impossible que tu décide à un moment donné d'en prévoir d'autres, voire, de prévoir la possibilité pour certaines d'entre-elles, d'être améliorées.

    Il n'est pas impossible que tu décide, à un moment donné, de faire la distinction entre les armes "à une main" (les épées, et les haches "courtes") et les armes "à deux mains" (les arcs, certains épées ou haches longues, les lances,...)

    Voire, comme je viens de parler d'arc, que tu veuille faire la distinction entre les armes "de mélée" (corps à corps) et les armes "à distance"...

    Tu me diras sans doute que toute cette réflexion revient, en gros, à tirer des plans sur la comète...

    Mais, si tu ne prévois pas ce genre d'évolution dés le départ (cela ne veut pas dire que tu dois directement mettre ces évolution en oeuvre, uniquement que tu dois te laisser l'occasion d'évoluer ), tu auras énormément de mal à faire évoluer ton projet si l'une ou l'autre de ces évolutions vient à s'avérer intéressante ou utile

    Mais n'allons pas trop vite en besogne, et intéressons nous à ce que tu veux pour l'instant...

    Tu veux donc un combat, dont nous avons déterminé que, comme il doit gérer les combattants, il doit prendre en charge la création des armes que les combattants utilisent.

    Comme il doit gérer les combattants, il doit:
    • Pouvoir créer les combattants et les armer
    • Pouvoir être prévenu lorsqu'un combattant meure (ce qui met fin au combat )

    Tu auras donc une classe 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
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    /* indiquons juste ici qu'il existe un type "Combattant" et un type "Arme" (1)
     */
    class Combattant;
    class Arme;
    class Combat
    {
           /* cela risque de changer par la suite, mais, pour l'instant
            * il ne te faut "que" deux combattants
            */
           /* Vu  qu'on ne sais pas, dés le départ, quelle arme chaque
            * combattant utilisera, ils ne peuvent pas être créés "d'office"
            *  (dans le constructeur du combat)
            */
        Combattant * m_premier;
        Combattant * m_second;
        /* une variable qui indique si le combat doit continuer */
        bool encore;
        public:
            /* Le constructeur rappelle que les deux combattants n'existent
             * pas encore
             */
           Combat():m_premier(0),m_second(0), encore(false){} /* (2) */
           /* lorsque l'on détruit le combat, on détruit les combattants,
            * car ils n'ont aucune raison de continuer à exister ;)
            */
           ~Combat()
           {
                delete m_premier;
                delete m_second;
            }
           /* on doit pouvoir lui demander de créer un combattant 
            * en indiquant si c'est le premier ou le second
            */
           bool creerCombattants(int);
           /* on doit pouvoir le prévenir qu'un combattant est mort */
           void combattantMeurt(Combattant*);
           /* on doit pouvoir lui demander de détruire le(s)
            * combattant(s) restant(s)
            */
           void detruitCombattants();
           /* on doit pouvoir lui donner le  signal de début de combat */
           void debutCombat();
           /* et enfin, on doit pouvoir obtenir l'adversaire d'un combattant */
           Combattant adversaire(Combattant * qui)
           {return qui==m_premier? m_second: m_premier; /* (3) */}
    };
    NOTA:
    (1) Cela s'appelle une déclaration anticipée et permet juste au compilateur de savoir qu'un type existe... la FAQ a une entrée sur le sujet
    (2) représente ce que l'on appelle une liste d'initialisation... Fais un tour du coté de la FAQ, tu trouvera toutes les informations utiles sur le sujet
    (3) cette syntaxe est ce qu'on appelle l'opérateur ternaire et correspond à un if else (il ne faut pas en abuser, mais ici, elle est intéressante
    Dans le fichier combat.cpp, nous trouverons:
    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
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
     
    /* nous allons utiliser une "fabrique d'armes" de manière à ce que 
     * le combat ne doive pas connaitre l'ensemble des armes existantes
     * (il doit juste savoir que ce sont des armes ;))
     */
    /* Pour la définition de la classe, le seul fait de savoir qu'il un type
     * existe Arme et un type Combattant suffisait...
     *
     * une déclaration anticipée était suffisante
     * ici, nous devons tout savoir sur sur le type Combattant...
     *
     * Par contre, pour ce qui concerne l'arme, nous devons juste savoir
     * que notre fabrique d'arme la fournit
     */
    #include <Combattant.h>
     
    #include <FabriqueArme.h>
    /* nous avons besoin de cin et de cout */
    #include <iostream>
    /* numCombattant: le numéro d'ordre du combattant
     *                (1 pour m_premier 2 pour m_second)
     * renvoie : vrai si le combattant a été créé
     *           faux autrement          
     */   
    bool Combat::creerCombattant(int  numCombattant)
    {
        /* si le combattant existe déjà, ou si on demande de créer
         * un combattant dont le numéro est supérieur à deux
         * on refuse de le créer et on 
         * renvoie false
         */
        if((numCombattant==1 && m_premier) || 
           (numCombattant==2 && m_second) ||
           numCombattant >2)
            return false;
        /* si on arrive ici, on peut créer notre combattant 
         * Il nous faut son arme... C'est la fabrique d'armes
         * qui s'en charge
         */
        Arme* arme=FabriqueArmes::creeArme();
        /* nous créons notre combattant en lui donnant l'arme de son
         * choix
         */
        Combattant * cbt =new Combattant(arme);
        /* et nous l'assignons à sa place */
        if(nbCombattant==1)
            m_premier = cbt;
        else
            m_second = cbt;
        /* on indique que tout s'est bien passé ;) */
        return true;
    }
    void Combat::CombattantMeure(Combattant *mort)
    {
        /* tiens, amusons nous et gardons trace du vainqueur */
        int vainqueur;
        /*Quand un combattant meure, il est détruit */
        if(mort==m_premier)
        {
            delete m_premier;
            m_premier = 0;
            vainqueur = 2;
        }
        else
        {
            delete m_second ;
            m_second = 0;
            vainqueur = 1;
        }
        /* le combat s'arrete */
        encore = 0;
        /* et on annonce le vainqueur */
        std::cout<<" !!! And the winner is... "<<vainqueur
                 <<"  !!! "<<std::endl;
    }
    void Combat::detruitCombattants()
    {
        /* détruits les combattants */
        delete m_premier;
        delete m_second;
        /* s'assurer que nous n'essayerons pas de les détruire une
         * seconde fois
         */
        m_premier = 0;
        m_second = 0;
        /* et met le combat dans un état d'arrêt */
        encore = false;
    }
    /*Le début du combat est aussi le déroulement du combat */
    void Combat::debutCombat()
    {
        /* ici, j'envisage un mode "tour à tour", le premier choix
         * au premier combattant
         */
        Combattant * un =m_premier;
        Combattant * deux = m_second;
        /* le combat commence */
        encore = true;
        while (encore)
        {
            /* celui qui a la main joue */
            un->jouer(this);
            /* et on inverse */
            std::swap(un,deux);
        }
    }
    La fabrique d'armes serait composée d'une seule fonction, qui créera l'arme d'après le choix de l'utilisateur.

    Comme il ne nous sert à rien de disposer d'une instance de la fabrique d'armes, la fonction sera déclarée statique.

    Cela nous donne 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
    /* nous devons, ici, juste prévenir le compilateur qu'il existe
     * un type "Arme"
     */
    class Arme;
    class FabriqueArmes
    {
        private:
            /* on empêche la construction, la copie, l'assignation et la 
             * destruction de la fabrique d'arme en déclarant le constructeur,
             * le constructeur par copie, l'opérateur d'assignation et le 
             * destructeur privés et en ne les implémentant pas
             */
            FabriqueArmes(); 
            ~FabriqueArmes();
            FabriqueArmes(FabriqueArmes const &);
            FabriqueArmes& operator=(FabriqueArmes const&);
        public:
            static Arme* creeArme();
    };
    et l'implémentation (FabriqueArme.cpp) sera 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
    /* nous avons besoin de cin et de cout */
    /* nous incluons ici tous les fichiers d'en-tête qui définissent nos armes */
    #include <EpeeBronze.h>
    #include <HacheFer.h>
    #include <Lance.h>
    /* si tu décide de créer de nouvelles armes, il "suffira" de rajouter
     * le fichier d'en-tête correspondant et de mettre cette fonction à jour ;)
     */
    Arme * creeArme()
    {
        std::cout << "Quel arme choisissez-vous pour le joueur "
                  << numJoueur<< " ?" << end
                  << "1 - La lance (dégats : 18 pts)" << endl
                  << "2 - L'epée en bronze (dégats : 19 pts)" << endl
                  << "3 - La hache de fer (dégats : 20 pts)" <<  endl
                  << "Votre choix : ";
        std::cin >> choixArme;
        return (choixArme==1 ? 
                    new Lance : 
                    choixArme==3 ? 
                        new HacheFer : 
                    new EpeeBronze);
    }
    Le combattant va donc recevoir une Arme créée par la fabrique d'armes (et dont il n'a, en définitive que faire de savoir de quel type elle est, vu que le joueur l'a choisie) pour laquelle il sera responsable de la destruction.

    On doit pouvoir lui faire savoir qu'il subit des dégâts (que, par la suite, tu peux décider d'atténuer en fonction de son équipement) et lui donner la possibilité de jouer (qui pourrait lui donner la possibilité de se mettre en mode "défensif")

    Cela prendrait 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
    /* les déclarations anticipées nécessaires */
    class Combat;
    class Arme;
    class Combattant
    {
        private:
            /* il a une arme */
            Arme m_arme:
            /* et des points de vie et de mana */
            int m_vie;
            int m_mana;
        public:
           /* le constructeur */
           Combattant(Arme*);
           /* et le destructeur */
           ~Combattant();
           /* La possibilité de jouer */
           void jouer(Combat*);
           /* la possibilité de lui infliger des dégats 
            * Si il meure, il doit prévenir le combat...
            */
           void subirAttaque(int, Combat* );
           /* la possibilité de l'interroger sur ses points de vie */
           int pointVie() const{return m_vie;}
           /* la possibilité de l'interroger sur ses points de mana */
           int pointMana() const{return m_mana;}
    };
    qui serait implémenté (Combattant.cpp) 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
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    /* nous devons connaitre la définition de Combattant ainsi que
     *le contenu de combat, pour lui demander de nous rappeler l'adversaire
     */
    #include <Combattant.h>
    #include <Combat.h>
    /* On a aussi besoin de savoir les fonctions que l'on peut utiliser avec
     * notre arme... mais on n'a absolument pas besoin de connaitre l'ensemble
     * des armes, c'est pourquoi, seule l'inclusion de <Arme.h> est utile ici
     */
    #include <Arme.h>
    /* Lorsqu'il est créé, le combattant a 100 points de vie et 80 points de mana
     * (décidé tout à fait arbitrairement ;)), et on lui donne une arme
     */
    Combattant::Combattant(Arme* a):m_arme(a),m_vie(100),m_mana(80)
    {
        /* plus tard, il sera sans doute utile de préciser à l'arme
         * qui la tient en main sous une forme proche de
         * m_arme->setUser(this);
         */
    }
    /* Quand le combattant est détruit, on détruit son arme */
    Combattant::~Combattant()
    {
        delete m_arme;
    }
    void Combattant::jouer(Combat* cbt)
    {
        /* Nous pourrions modifier le comportement pour permettre
         * au combattant de se mettre en position défensive, ou
         * d'utiliser un sort de soin, ou ... que sais-je...
         *
         * Dans l'immédiat, jouer revient à attaquer son aversaire
         * (mais c'est l'arme qui s'en occupe)
         */
         m_arme->attaquer(cbt->adversaire(this), cbt); /* (4) */
    }
    void Combattant::subirAttaque(int degats, Combat* cbt)
    {
        /* ici aussi, il est possible de modifier le comportement par la suite...
         * à la base, le combattant subit les dégats de plein fouet
         * et prévient le combat qu'il meure si ses points de vie 
         * atteignent 0 ou moins
         */
        m_vie-=degat;
        if(m_vie<=0)
            cbt->combattantMeurt(this);
    }
    Nota
    (4) demande à l'arme tenue par le combattant en cours d'attaquer le combattant que le combat désigne comme étant l'adversaire du combattant en cours... Cela revient à peut de choses près (en tous les cas dans le fond) au même que si on avait écrit:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    Combattant * temp=cbt->aversaire(this);
    m_arme->attaquer(temp,cbt);
    Il reste enfin le problème de nos armes...

    Si tu veux te contenter d'une seule structure nommée Arme pour gérer tous les types d'armes actuels et futurs, tu va en perdre ton latin parce que tu ne tardera pas à te rendre compte que certaines armes doivent avoir un comportement adapté qui leur est propre...

    Par contre, on peut jouer sur le fait que, bien qu'une épée et une lance soient toutes les deux des armes, une épée n'est pas une lance, ni inversement.

    Jusqu'à présent, nous avons travaillé avec des relations "faibles" entre nos objets où l'un disposait de l'autre (le combat dispose du combattant et le combattant dispose de l'arme), que nous appelons en programmation aggrégation.

    Ici, nous allons utiliser la relation la plus forte qui existe en programmation: l'héritage.

    Cela permet d'indiquer explicitement qu'un type particulier (la lance ou l'épée, par exemple) est une arme.

    C'est à dire que, bien que la lance ou l'épée aient toutes les deux des caractéristiques qui leur sont propres, nous pouvons appeler tous les comportements que l'on peut attendre d'une arme.

    L'arme est ici tout à fait générique, alors que la lance ou l'épée sont considérées comme des armes... "spécialisées".

    La classe la plus générique (ici l'arme) est appelée "classe mère", "classe de base", ou, si il y a plusieurs niveaux d'héritage "classe ancêtre".

    Inversément, la classe spécialisée est appelée "classe enfant" ou "classe dérivée".

    Il n'est pas impossible d'envisager plusieurs niveaux d'héritages, par exemple:
    • Nous aurions une classe Arme qui serait l'ancêtre de toutes les autres
    • Nous aurions ArmeUneMain et ArmeDeuxMains qui descendraient directement de Arme (la différence entre les deux serait l'utilisation d'une ou de deux mains pour la manier)
    • Nous aurions ensuite des épées et des haches "suffisamment légères" pour être maniée à une main et d'autres "tellement lourdes" qu'elles devraient l'être ) deux mains (HacheLegere dérivant de ArmeUneMain et EpeeLourde dérivant de ArmeDeuxMains)
    • Il serait ensuite possible de dériver nos haches et nos épées en fonction de leur forme, de leur origine ou de la matière dont elles sont faite de HacheLourde, HacheLegere, EpeeLegere ou EpeeLourde, selon la catégorie à laquelle la hache ou l'épée se raccroche
    • Les combinaisons du genre ont pour seule limite ta propre inspiration


    Simplement, il n'est pas exclu que nous décidions d'adapter certains des comportements que l'on peut attendre d'une arme au type particulier d'arme réellement utilisé.

    Il y a cependant une restriction importante: Si on fait passer une épée pour une arme (sans précision), nous ne pourrons appeler que les comportements propres aux armes (sans précision), et, si l'épée dispose d'un comportement supplémentaire, nous ne pourrons pas utiliser ce comportement sans "retransformer" (on parle de transtyper ou de caster) notre arme en... épée...

    Une telle adaptation s'appelle le polymorphisme (on parle aussi de comportement polymorphe ou polymorphique)

    Lorque l'on identifie un comportement devant être polymorphe, on le signale au compilateur en déclarant la fonction virtual (on parle alors de fonction virtuelle ou de méthode)

    Si, pour une raison ou une autre, on se rend compte qu'il faut disposer d'un comportement (comprend: être en mesure d'appeler la fonction correspondante) dans la classe de base alors que nous ne disposons pas des données nécessaires pour implémenter le comportement (la raison la plus fréquente est que les données nécessaires n'ont "rien à faire" dans la classe de base), nous pouvons rendre une méthode virtuelle pure, ce qui se fait en rajoutant un " = 0" après la déclaration de la méthode (qui est, rappelons le, une fonction virtuelle )

    Seulement, le compilateur a horreur du vide, et, s'il rencontre une méthode virtuelle pure, il t'interdira de créer une instance de la classe, ainsi que de toute classe dérivée qui ne définirait pas la méthode virtuelle pure.

    Nous parlons alors de classe abstraite (par opposition aux classes concrètes qui peuvent être instanciées)

    Inversement, si tu veux interdire à l'utilisateur de créer une instance d'une classe de base, il te suffira de créer une méthode virtuelle pure.

    La méthode de choix dans ce cas là est... le destructeur de la classe (que tu définira quand même s'il y a des choses à faire dedans, mais que tu peux déclarer virtuel pur)

    Nous utilisons régulièrement le terme d'interface lorsqu'il s'agit de désigner une classe qui présente principalement des fonctions virtuelles pures.

    La classe Arme dans notre exemple est un candidat idéal à être une telle interface.

    Et si un jour tu en viens à faire la distinction entre les armes à une main et les armes à deux main, ArmeUneMain et ArmeDeuxMains seront des candidates idéales pour rester abstraites (elle hériteront de cette qualité par le fait qu'elles n'implémenteront sans doutes pas tous les comportement de la classe Arme).

    Tu trouvera pour ces concepts (héritage et classe abstraites) pas mal d'informations dans la FAQ

    Maintenant que la théorie de base a (très rapidement, je l'accorde) été abordée, il est temps de nous intéresser à ce que l'on attend réellement de notre arme (comprend: de l'arme dont on ne connait a priori pas le type réel)...

    Hé bien, nous allons attendre d'elle... qu'elle attaque notre adversaire...
    Et, tant qu'à faire, qu'elle nous permette de calculer qu'elle est capable d'infliger.

    Par la suite, tu pourrais vouloir permettre d'apporter des améliorations, savoir qui l'utilise, l'utiliser pour parer une attaque, ou la vendre... Mais ca, c'est encore pures supputations

    Bien que nous ne sachions pas quel sera le type réel de l'arme, si nous faisons en sorte que le calcul des dégâts soit un comportement polymorphe, nous pouvons parfaitement déterminer un comportement d'attaque de l'adversaire qui ne changera pas quel que soit le type réel de l'arme.

    Par contre, nous allons partir du principe que nous ne disposons d'aucune information permettant d'implémenter le calcul des dégâts pour une arme "générique"...

    La fonction correspondante sera donc déclarée virtuelle pure, ce qui rendra notre classe Arme abstraite.

    Voici à quoi elle pourrait très bien ressembler:
    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
     
    /* toujours nos déclarations anticipées */
    class Combat;
    class Combattant;
    class Arme
    {
        public:
            virtual ~Arme(); /* nécessaire pour assurer le polymorphisme, même
                              * s'il ne fait rien
                              */
           void attaquer(Combattant*, Combat) const ;
           /* le comportement polymorphe intéressant */
           virtual int calculerDegats() const = 0; /* ce const permet juste de dire
                                        * que la fonction ne modifie pas l'objet ;)
                                        * le = 0 indique que nous n'implémentons pas 
                                        * la fonction, ce qui va de facto rendre la classe
                                        * abstraite 
                                        */
    };
    qui présentera les implémentations (Armes.cpp) proches 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
    /* Il nous faut la définition de la classe Arme ainsi que celle de la classe
     * Combattant
     */
    #include <Arme.h>
    #include <Combattant.h>
    /* le destructeur ne fait strictement rien 
     * mais, comme il a été déclaré, il faut l'implémenter
     */
    Arme::~Arme(){}
    /* Nous savons exactement ce que fera attaquer 
     * Par contre, si un jour, tu décidais que le fait d'attaquer ton
     * adversaire réduit la résistance ou les dégats infligés par l'arme
     * à l'attaque suivante, il faudrait enlever le const 
     */
    void attaquer(Combattant* adversaire, Combat * cbt) const 
    {
        adversaire->subirAttaque(calculerDegats(),cbt);
    }
    /* nous ne sommes pas en mesure d'implémenter calculerDegat() 
     * parce qu'on ne dispose d'aucune donnée pour le faire
     */
    Il ne nous reste plus qu'à créer nos classes d'armes réelles (ici, je vais montrer la manière avec EpeeBronze, mais ce sera pareil pour toutes les autres armes, à moins que tu ne décide de distinguer les armes à une main et les armes à deux mains )
    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
    /* Ici, la déclaration anticipée ne suffit plus: il faut inclure l'en-tete
     * Arme.h
     */
    #include <Arme.h>
    class EpeeBronze : public Arme /* on signale que EpeeBronze hérite de
                              * de manière publique de Arme
                              */
    {
        private:
            /* les dégâts d'origine qui seront infligés */
            int m_degats;
        public:
            /* le constructeur */
            EpeeBronze();
            /* et le destructeur (qui doit être virtuel )*/
            virtual ~EpeeBronze();
            /* et, surtout, n'oublions pas le comportement polymorphe */
            virtual int claculerDegats() const;
     
    }
    et l'implémentation qui va avec (EpeeBronze.cpp):
    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
    /*Tout ce dont nous avons besoin ici, c'est... de la définition de EpeeBronze 
     * :D
     */
    #include <EpeeBronze.h>
    /* définissons le constructeur (les dégats par défaut ont été arbitrairement
     * définis à 19 ;) )
     */
    EpeeBronze::EpeeBronze():m_degats(19){}
    /* Le destructeur ne fait toujours rien, mais nous devons
     * le définir, vu que nous l'avons déclaré 
     */
    EpeeBronze::~EpeeBronze(){}
    /* et enfin le comportement polymorphe */
    int EpeeBronze::calculerDegats() const
    {
        /* Il n'est pas exclu que, par la suite, du modifie la manière
         * de calculer les dégâts, pour l'instant, nous renvoyons
         * simplement la valeur de base ;)
         */
        return m_degats;
    }
    Il ne reste "plus" qu'à utiliser le tout dans la fonction principale...
    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
    /* tout ce qu'il nous faut ici, c'est la définition de Combat... */
    #include <Combat.h>
    int main()
    {
       /* Nous créons un combat  */
       Combat cbt;
       /* nous lui demandons de créer le premier combattant */
       cbt.creerCombattant(1);
       /* puis le deuxième */
       cbt.creerCombattant(2);
       /* et maintenant, que le combat commence :D */
       cbt.debutCombat();
       /* il y a eu un mort, et le vainceur a été désigné... 
        * il va partir (sans avoir été soigné, le pauvre) sur un deuxième combat...
        */
       if(!cbt.creerCombattant(1))
           cbt.creerCombattant(2);
       /* c'est reparti :D */
       cbt.debutCombat();
       /* Allez, soyons sympa, permettons aux deux joueurs de partir sur
        * un pied d'égalité
        */
      cbt.detrutCombatant();
      /*...*/
      return 0;
    }
    Et voilà... Un véritable roman qui, je l'espère, t'aura donné les informations qui te manquaient en plus de te donner un aperçu d'une manière correcte d'envisager ton projet.

    PS: d'habitude, je mets des liens vers la FAQ, mais là, je viens de passer pres de deux heures à écrire ce message, et j'ai un peu la flegme... Sorry

    Par contre, si je suis passé un peu trop vite sur quelque chose, n'hésite pas à demander des informations supplémentaires
    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

  11. #11
    Invité
    Invité(e)
    Par défaut
    Citation Envoyé par beware Voir le message
    ET dans l'absolu, c'est une géne ou pas?
    Pas forcément, mais c'est un risque. Si ton but est juste d'indiquer quel personnage participe au combat, il vaut mieux mettre une référence (c'est à dire un "lien") vers le personnage concerné, dans ta classe Combat, plutôt que d'y recopier le personnage tout entier. Si tu recopies le personnage, tu vas devoir recopier et gérer toutes ses données, en particulier quand elles changent...

    Par exemple, si le personnage perd des points de vie, tu vas devoir le mettre à jour dans l'enregistrement personnage, mais aussi dans celui contenu dans le combat. Si tu as recopié le personnage dans plusieurs autres données (arme, partie, groupe, donjon,...), tu vas devoir tout suivre et mettre à jour...

    C'est exactement l'idée des références et des pointeurs : ils ne "contiennent" pas de données mais, font référence, ou pointent sur, un personnage... Je pense que c'est ce qu'il faudrait que tu utilises...

    Tu vas donc te retrouver avec

    1- des personnages, bien rangés dans une "boite à personnages" (on appele cela un Conteneur)
    2- des combats, bien rangés dans une "boîte à combats"

    et les combats de chaque personnage, ou les personnages de chaque combat seront représentés par des liens (pointeurs ou références) à l'intérieur de la classe correspondante.

    Francois

  12. #12
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Novembre 2005
    Messages
    93
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Novembre 2005
    Messages : 93
    Points : 36
    Points
    36
    Par défaut
    Citation Envoyé par koala01 Voir le message
    Là est peut être tout le problème... (soit dit sans vouloir t'offencer)

    Tout à "l'excitation" d'apprendre un langage de programmation, j'ai l'impression que tu te lances dans un projet pour lequel tu n'as qu'une idée bien trop imprécise de ce que tu veux, et de la manière de l'obtenir, alors qu'il te manque des concepts "de bases".

    Il n'est donc pas impossible que, pour un premier projet, tu ne sois occupée à viser "un peu trop haut" par rapport à tes connaissances .

    Comprenons nous bien, le but de ces écrit n'est pas de te décourager de mener ton projet à bien, le but est de te faire prendre conscience que, si tu démarres un projet en n'ayant pas les bases suffisantes, tu risques très fort soit d'être bloquée en cours de route, soit, parce que (c'est du moins tout le mal que je te souhaites) tes connaissances auront évolué et que tu en sera réduite, pour faire évoluer ton projet, à "tout casser"... Ce qui est particulièrement agaçant
    Je ne me suis pas offenser et il est vrai, je le reconnais, que j'ai foncé tête baissée sur le clavier sans réfléchir à tout ce dont j'avais besoin.

    Et sans établir un scénario aussi poussé que le tien et c'est la aussi un oubli de ma part.

    Je crois donc qu'il faut je continue les chapitres du cours pour mieux utiliser les possibilités du C++.

    Je vais également lire attentivement ton code pour trier ce que je comprends et chercher une explication pour ce que je ne comprends pas.

    En tout cas, vraiment MERCI à toi pour cette longue et instructive réponse.

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

Discussions similaires

  1. Constructeurs / Destructeurs / Classes imbriquées
    Par XAMLdev dans le forum Langage
    Réponses: 4
    Dernier message: 29/07/2013, 19h55
  2. [POO] Constructeur de classe
    Par cescu dans le forum Langage
    Réponses: 7
    Dernier message: 14/02/2006, 21h50
  3. Suite Thread Simultanés: instances de classe differentes?
    Par macgile dans le forum Framework .NET
    Réponses: 3
    Dernier message: 04/01/2006, 09h50
  4. PB d'update qui plante aléatoirement sans renvoyer d'erreur
    Par plc402 dans le forum MS SQL Server
    Réponses: 5
    Dernier message: 01/08/2005, 09h10
  5. Réponses: 7
    Dernier message: 15/07/2005, 15h07

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