Mis à jour le 04/12/2018
  • 50 heures
  • Difficile

Ce cours est visible gratuitement en ligne.

Ce cours existe en livre papier.

Vous pouvez obtenir un certificat de réussite à l'issue de ce cours.

Vous pouvez être accompagné et mentoré par un professeur particulier par visioconférence sur ce cours.

J'ai tout compris !

Créez les classes (Partie 1/2)

Connectez-vous ou inscrivez-vous gratuitement pour bénéficier de toutes les fonctionnalités de ce cours !

Au chapitre précédent, vous avez vu que la programmation orientée objet pouvait nous simplifier la vie en « masquant », en quelque sorte, le code complexe. C'est un des avantages de la POO mais ce n'est pas le seul, comme vous allez le découvrir petit à petit : les objets sont aussi facilement réutilisables et modifiables.

À partir de maintenant, nous allons apprendre à créer des objets. Vous allez voir que c'est tout un art et que cela demande de la pratique. Il y a beaucoup de programmeurs qui prétendent faire de la POO et qui le font pourtant très mal. En effet, on peut créer un objet de 100 façons différentes et c'est à nous de choisir à chaque fois la meilleure, la plus adaptée. Ce n'est pas évident, il faut donc bien réfléchir avant de se lancer dans le code comme des forcenés.

Allez, on prend une grande inspiration et on plonge ensemble dans l'océan de la POO !

Créez une classe

Commençons par la question qui doit vous brûler les lèvres.

Je croyais qu'on allait apprendre à créer des objets, pourquoi tu nous parles de créer une classe maintenant ?
Quel est le rapport ?

Eh bien justement, pour créer un objet, il faut d'abord créer une classe !
Je m'explique : pour construire une maison, vous avez besoin d'un plan d'architecte non ? Eh bien imaginez simplement que la classe c'est le plan et que l'objet c'est la maison.

« Créer une classe », c'est donc dessiner les plans de l'objet.

Une fois que vous avez les plans, vous pouvez faire autant de maisons que vous voulez en vous basant sur ces plans. Pour les objets c'est pareil : une fois que vous avez fait la classe (le plan), vous pouvez créer autant d'objets du même type que vous voulez.

Une fois la classe créée, on peut construire des objets
Une fois la classe créée, on peut construire des objets

Créez une classe, oui mais laquelle ?

Avant tout, il va falloir choisir la classe sur laquelle nous allons travailler.

Reprenons l'exemple sur l'architecture : allons-nous créer un appartement, une villa avec piscine, un loft spacieux ?
En clair, quel type d'objet voulons-nous être capables de créer ?

Les choix ne manquent pas. Je sais que, quand on débute, on a du mal à imaginer ce qui peut être considéré comme un objet. La réponse est : presque tout !

Vous allez voir, vous allez petit à petit avoir le feeling qu'il faut avec la POO. Puisque vous débutez, c'est moi qui vais choisir (vous n'avez pas trop le choix, de toute façon !).
Pour notre exemple, nous allons créer une classePersonnagequi va représenter un personnage de jeu de rôle (RPG).

Bon, on la crée cette classe ?

C'est parti.

Pour commencer, je vous rappelle qu'une classe est constituée (n'oubliez pas ce vocabulaire, il est fon-da-men-tal !) :

  • de variables, ici appelées attributs (on parle aussi de variables membres) ;

  • de fonctions, ici appelées méthodes (on parle aussi de fonctions membres).

Voici le code minimal pour créer une classe :

class Personnage
{
    
}; // N'oubliez pas le point-virgule à la fin !

Comme vous le voyez, on utilise le mot-clé class.
Il est suivi du nom de la classe que l'on veut créer. Ici, c'estPersonnage.

Nous allons écrire toute la définition de la classe entre les accolades. Tout ou presque se passera donc à l'intérieur de ces accolades.
Et surtout, très important, le truc qu'on oublie au moins une fois dans sa vie : il y a un point-virgule après l'accolade fermante !

Ajout de méthodes et d'attributs

Bon, c'est bien beau mais notre classe Personnageest plutôt... vide.
Que va-t-on mettre dans la classe ? Vous le savez déjà voyons.

  • des attributs : c'est le nom que l'on donne aux variables contenues dans des classes ;

  • des méthodes : c'est le nom que l'on donne aux fonctions contenues dans des classes.

Le but du jeu, maintenant, c'est justement d'arriver à faire la liste de tout ce qu'on veut mettre dans notrePersonnage. De quels attributs et de quelles méthodes a-t-il besoin ? C'est justement l'étape de réflexion, la plus importante. C'est pour cela que je vous ai dit au début de ce chapitre qu'il ne fallait surtout pas coder comme des barbares dès le début mais prendre le temps de réfléchir.

Par quoi commencer : les attributs ou les méthodes ? Il n'y a pas d'ordre, en fait, mais je trouve un peu plus logique de commencer par voir les attributs puis les méthodes.

Les attributs

C'est ce qui va caractériser votre classe, ici le personnage. Ce sont des variables, elles peuvent donc évoluer au fil du temps. Mais qu'est-ce qui caractérise un personnage de jeu de rôle ? Allons, un petit effort.

  • Par exemple, tout personnage a un niveau de vie. Hop, cela fait un premier attribut : vie ! On dira que ce sera unintet qu'il sera compris entre 0 et 100 (0 = mort, 100 = toute la vie).

  • Dans un jeu de rôle, il y a le niveau de magie, aussi appelé mana. Là encore, on va dire que c'est unintcompris entre 0 et 100. Si le personnage a 0 de mana, il ne peut plus lancer de sort magique et doit attendre que son mana se recharge tout seul au fil du temps (ou boire une potion de mana !).

  • On pourrait rajouter aussi le nom de l'arme que porte le joueur : nomArme. On va utiliser pour cela unstring.

  • Enfin, il me semble indispensable d'ajouter un attribut degatsArme, unintqui indiquerait cette fois le degré de dégâts que porte notre arme à chaque coup.

On peut donc déjà commencer à compléter la classe avec ces premiers attributs :

class Personnage
{
    int m_vie;
    int m_mana;
    string m_nomArme;
    int m_degatsArme;
};

Deux ou trois petites choses à savoir sur ce code :

  • Ce n'est pas une obligation mais une grande partie des programmeurs (dont moi) a l'habitude de faire commencer tous les noms des attributs de classe par « m_ » (le « m » signifiant « membre », pour indiquer que c'est une variable membre, c'est-à-dire un attribut). Cela permet de bien différencier les attributs des variables « classiques » (contenues dans des fonctions par exemple).

  • Il est impossible d'initialiser les attributs ici. Cela doit être fait via ce qu'on appelle un constructeur, comme on le verra un peu plus loin.

  • Comme on utilise un objet string, il faut bien penser à rajouter un#include <string>dans votre fichier.

La chose essentielle à retenir ici, c'est que l'on utilise des attributs pour représenter la notion d'appartenance. On dit qu'un Personnagea une vie et a un niveau de magie. Il possède également une arme. Lorsque vous repérez une relation d'appartenance, il y a de fortes chances qu'un attribut soit la solution à adopter.

Les méthodes

Les méthodes, elles, sont grosso modo les actions que le personnage peut effectuer ou qu'on peut lui faire faire. Les méthodes lisent et modifient les attributs.

Voici quelques actions réalisables avec notre personnage :

  • recevoirDegats: le personnage prend un certain nombre de dégâts et donc perd de la vie.

  • attaquer: le personnage attaque un autre personnage avec son arme. Il inflige autant de dégâts que son arme le lui permet (c'est-à-diredegatsArme).

  • boirePotionDeVie: le personnage boit une potion de vie et regagne un certain nombre de points de vie.

  • changerArme: le personnage récupère une nouvelle arme plus puissante. On change le nom de l'arme et les dégâts qui vont avec.

  • estVivant: renvoietruesi le personnage est toujours vivant (il possède plus que 0 point de vie), sinon renvoiefalse.

C'est un bon début, je trouve.

On va rajouter cela dans la classe avant les attributs (en POO, on préfère présenter les méthodes avant les attributs, bien que cela ne soit pas obligatoire) :

class Personnage
{
    // Méthodes
    void recevoirDegats(int nbDegats)
    {

    }

    void attaquer(Personnage &cible)
    {

    }

    void boirePotionDeVie(int quantitePotion)
    {

    }

    void changerArme(string nomNouvelleArme, int degatsNouvelleArme)
    {

    }

    bool estVivant()
    {

    }

    // Attributs
    int m_vie;
    int m_mana;
    string m_nomArme;
    int m_degatsArme;    
};

Ceci dit, vous devriez déjà avoir une petite idée de ce que vous allez mettre dans ces méthodes.

Par exemple,recevoirDegatsretranchera le nombre de dégâts (indiqués en paramètre parnbDegats) à la vie du personnage.
La méthodeattaquerest également intéressante : elle prend en paramètre... un autre personnage, plus exactement une référence vers le personnage cible que l'on doit attaquer ! Et que fera cette méthode, à votre avis ? Eh oui, elle appellera la méthoderecevoirDegatsde la cible pour lui infliger des dégâts.

Vous commencez à comprendre un peu comme tout cela est lié et terriblement logique ?
On met en général un peu de temps avant de correctement « penser objet ». Si vous vous dites que vous n'auriez pas pu inventer un truc comme cela tout seul, rassurez-vous : tous les débutants passent par là. À force de pratiquer, cela va venir.

Pour info, cette classe ne comporte pas toutes les méthodes que l'on pourrait y créer : par exemple, on n'utilise pas de magie ici. Le personnage attaque seulement avec une arme (une épée par exemple) et n'emploie donc pas de sort. Je laisse exprès quelques fonctions manquantes pour vous inciter à compléter la classe avec vos idées.

En résumé, un objet est bel et bien un mix de variables (les attributs) et de fonctions (les méthodes). La plupart du temps, les méthodes lisent et modifient les attributs de l'objet pour le faire évoluer.
Un objet est au final un petit système intelligent et autonome, capable de surveiller tout seul son bon fonctionnement.

Droits d'accès et encapsulation

Nous allons maintenant nous intéresser au concept le plus fondamental de la POO : l'encapsulation. Ne vous laissez pas effrayer par ce mot, vous allez vite comprendre ce que cela signifie.

Tout d'abord, un petit rappel. En POO, il y a deux parties bien distinctes :

  • On crée des classes pour définir le fonctionnement des objets. C'est ce qu'on apprend à faire ici.

  • On utilise des objets. C'est ce qu'on a appris à faire au chapitre précédent.

Il faut bien distinguer ces deux parties car cela devient ici très important.

Création de la classe :

class Personnage
{
    // Méthodes
    void recevoirDegats(int nbDegats)
    {
 
    }
 
    void attaquer(Personnage &cible)
    {
 
    }
 
    void boirePotionDeVie(int quantitePotion)
    {
 
    }
 
    void changerArme(string nomNouvelleArme, int degatsNouvelleArme)
    {
 
    }
 
    bool estVivant()
    {
 
    }
 
    // Attributs
    int m_vie;
    int m_mana;
    string m_nomArme;
    int m_degatsArme;
};

Utilisation de l'objet :

int main()
{
    Personnage david, goliath;
    //Création de 2 objets de type Personnage : david et goliath
 
    goliath.attaquer(david); //goliath attaque david
    david.boirePotionDeVie(20); //david récupère 20 de vie en buvant une potion
    goliath.attaquer(david); //goliath réattaque david
    david.attaquer(goliath); //david contre-attaque... c'est assez clair non ?
    
    goliath.changerArme("Double hache tranchante vénéneuse de la mort", 40);
    goliath.attaquer(david);
 
 
    return 0;
}

Tenez, pourquoi n'essaierait-on pas ce code ?
Allez, on met tout dans un même fichier (en prenant soin de définir la classe avant lemain()) et zou !

#include <iostream>
#include <string>
 
using namespace std;
 
class Personnage
{
    // Méthodes
    void recevoirDegats(int nbDegats)
    {
 
    }
 
    void attaquer(Personnage &cible)
    {
 
    }
 
    void boirePotionDeVie(int quantitePotion)
    {
 
    }
 
    void changerArme(string nomNouvelleArme, int degatsNouvelleArme)
    {
 
    }
 
    bool estVivant()
    {
 
    }
 
    // Attributs
    int m_vie;
    int m_mana;
    string m_nomArme;
    int m_degatsArme;
};
 
int main()
{
    Personnage david, goliath;
    //Création de 2 objets de type Personnage : david et goliath
 
    goliath.attaquer(david);    //goliath attaque david
    david.boirePotionDeVie(20); //david récupère 20 de vie en buvant une potion
    goliath.attaquer(david);    //goliath réattaque david
    david.attaquer(goliath);    //david contre-attaque... c'est assez clair non ?
    
    goliath.changerArme("Double hache tranchante vénéneuse de la mort", 40);
    goliath.attaquer(david);
 
 
    return 0;
}

Compilez et admirez... la belle erreur de compilation !

Error : void Personnage::attaquer(Personnage&) is private within this context

Encore une insulte de la part du compilateur !

Les droits d'accès

On en arrive justement au problème qui nous intéresse : celui des droits d'accès (oui, j'ai fait exprès de provoquer cette erreur de compilation ; vous ne pensiez tout de même pas que ce n'était pas prévu ? ).

Ouvrez grand vos oreilles : chaque attribut et chaque méthode d'une classe peut posséder son propre droit d'accès. Il existe grosso modo deux droits d'accès différents :

  • public: l'attribut ou la méthode peut être appelé depuis l'extérieur de l'objet.

  • private: l'attribut ou la méthode ne peut pas être appelé depuis l'extérieur de l'objet. Par défaut, tous les éléments d'une classe sontprivate.

Concrètement, qu'est-ce que cela signifie ? Qu'est-ce que « l'extérieur » de l'objet ?
Eh bien, dans notre exemple, « l'extérieur » c'est lemain(). En effet, c'est là où on utilise l'objet. On fait appel à des méthodes mais, comme elles sont par défaut privées, on ne peut pas les appeler depuis lemain()!

Pour modifier les droits d'accès et mettre par exemplepublic, il faut taper « public » suivi du symbole « : » (deux points). Tout ce qui se trouvera à la suite serapublic.

Voici ce que je vous propose de faire : on va mettre en public toutes les méthodes et en privé tous les attributs.
Cela nous donne :

class Personnage
{
    // Tout ce qui suit est public (accessible depuis l'extérieur)
    public:
    
    void recevoirDegats(int nbDegats)
    {
 
    }
 
    void attaquer(Personnage &cible)
    {
 
    }
 
    void boirePotionDeVie(int quantitePotion)
    {
 
    }
 
    void changerArme(string nomNouvelleArme, int degatsNouvelleArme)
    {
 
    }
 
    bool estVivant()
    {
 
    }
 
    // Tout ce qui suit est privé (inaccessible depuis l'extérieur)
    private:
    
    int m_vie;
    int m_mana;
    string m_nomArme;
    int m_degatsArme;
};

Tout ce qui suit le mot-clé public:est public donc toutes nos méthodes sont publiques.
Ensuite vient le mot-clé private:. Tout ce qui suit ce mot-clé est privé donc tous nos attributs sont privés.

Voilà, vous pouvez maintenant compiler ce code et vous verrez qu'il n'y a pas de problème (même si le code ne fait rien pour l'instant). On appelle des méthodes depuis le main(): comme elles sont publiques, on a le droit de le faire.
En revanche, nos attributs sont privés, ce qui veut dire qu'on n'a pas le droit de les modifier depuis le main(). En clair, on ne peut pas écrire dans le main():

goliath.m_vie = 90;

Essayez, vous verrez que le compilateur vous ressort la même erreur que tout à l'heure : « ton bidule est private... bla bla bla... pas le droit d'appeler un élément private depuis l'extérieur de la classe ».

Mais alors... cela veut dire qu'on ne peut pas modifier la vie du personnage depuis lemain()? Eh oui ! C'est ce qu'on appelle l'encapsulation.

L'encapsulation

Moi j'ai une solution ! Si on mettait tout en public ? Les méthodes et les attributs, comme cela on peut tout modifier depuis lemain()et plus aucun problème ! Non ? Quoi j'ai dit une bêtise ?

Oh, trois fois rien. Vous venez juste de vous faire autant d'ennemis qu'il y a de programmeurs qui font de la POO dans le monde.

Il y a une règle d'or en POO et tout découle de là. S'il vous plaît, imprimez ceci en gros sur une feuille et placardez cette feuille sur un mur de votre chambre :

Encapsulation : tous les attributs d'une classe doivent toujours être privés.

Cela a l'air bête, stupide, irréfléchi, et pourtant tout ce qui fait que la POO est un principe puissant vient de là. Je ne veux pas en voir un seul mettre un attribut enpublic!

Voilà qui explique pourquoi j'ai fait exprès, dès le début, de mettre les attributs en privé. Ainsi, on ne peut pas les modifier depuis l'extérieur de la classe et cela respecte le principe d'encapsulation.

Vous vous souvenez de ce schéma du chapitre précédent ?

L'utilisation du code est simplifiée grâce à l'utilisation d'un objet
L'utilisation du code est simplifiée grâce à l'utilisation d'un objet

Les fioles chimiques, ce sont les attributs.
Les boutons sur la façade avant, ce sont les méthodes.

Et là, pif paf pouf, vous devriez avoir tout compris d'un coup. En effet, le but du modèle objet est justement de masquer à l'utilisateur les informations complexes (les attributs) pour éviter qu'il ne fasse des bêtises avec.

Imaginez par exemple que l'utilisateur puisse modifier la vie... qu'est-ce qui l'empêcherait de mettre 150 de vie alors que la limite maximale est 100 ? C'est pour cela qu'il faut toujours passer par des méthodes (des fonctions) qui vont d'abord vérifier qu'on fait les choses correctement avant de modifier les attributs.
Cela garantit que le contenu de l'objet reste une « boîte noire ». On ne sait pas comment cela fonctionne à l'intérieur quand on l'utilise et c'est très bien ainsi. C'est une sécurité, cela permet d'éviter de faire péter tout le bazar à l'intérieur.

Séparez prototypes et définitions

Bon, on avance mais on n'a pas fini !
Voici ce que je voudrais qu'on fasse :

  • séparer les méthodes en prototypes et définitions dans deux fichiers différents, pour avoir un code plus modulaire ;

  • implémenter les méthodes de la classePersonnage(c'est-à-dire écrire le code à l'intérieur parce que, pour le moment, il n'y a rien).

À ce stade, notre classe figure dans le fichiermain.cpp, juste au-dessus dumain(). Et les méthodes sont directement écrites dans la définition de la classe. Cela fonctionne, mais c'est un peu bourrin.

Pour améliorer cela, il faut tout d'abord clairement séparer lemain()(qui se trouve dansmain.cpp) des classes.
Pour chaque classe, on va créer :

  • un header (fichier*.h) qui contiendra les attributs et les prototypes de la classe ;

  • un fichier source (fichier*.cpp) qui contiendra la définition des méthodes et leur implémentation.

Je vous propose d'ajouter à votre projet deux fichiers nommés très exactement :

  • Personnage.h;

  • Personnage.cpp.

Vous devriez être capables de faire cela tous seuls avec votre IDE. Sous Code::Blocks, je passe par les menusFile>New File, je saisis par exemple le nomPersonnage.havec son extension et je réponds « Oui » quand Code::Blocks me demande si je veux ajouter le nouveau fichier au projet en cours (figure suivante).

Ajouter un fichier au projet

Personnage.h

Le fichier.hva donc contenir la déclaration de la classe avec les attributs et les prototypes des méthodes. Dans notre cas, pour la classePersonnage, nous obtenons :

#ifndef DEF_PERSONNAGE
#define DEF_PERSONNAGE

#include <string>

class Personnage
{
    public:

    void recevoirDegats(int nbDegats);
    void attaquer(Personnage &cible);
    void boirePotionDeVie(int quantitePotion);
    void changerArme(std::string nomNouvelleArme, int degatsNouvelleArme);
    bool estVivant();

    private:

    int m_vie;
    int m_mana;
    std::string m_nomArme; //Pas de using namespace std, il faut donc mettrestd:: devant string
    int m_degatsArme;
};

#endif

Comme vous pouvez le constater, seuls les prototypes des méthodes figurent dans le.h. C'est déjà beaucoup plus clair.

Personnage.cpp

C'est là qu'on va écrire le code de nos méthodes (on dit qu'on implémente les méthodes).
La première chose à ne pas oublier --sinon cela va mal se passer-- c'est d'inclure<string>etPersonnage.h.
On peut aussi rajouter ici unusing namespace std;. On a le droit de le faire car on est dans le.cpp(n'oubliez pas ce que je vous ai dit plus tôt : il faut éviter de le mettre dans le.h).

#include "Personnage.h"

using namespace std;

Maintenant, voilà comment cela se passe : pour chaque méthode, vous devez faire précéder le nom de la méthode par le nom de la classe suivi de deux fois deux points (::). PourrecevoirDegats, voici ce que nous obtenons :

void Personnage::recevoirDegats(int nbDegats)
{

}

Cela permet au compilateur de savoir que cette méthode se rapporte à la classePersonnage. En effet, comme la méthode est ici écrite en dehors de la définition de la classe, le compilateur n'aurait pas su à quelle classe appartenait cette méthode.

Personnage::recevoirDegats

Maintenant, c'est parti : implémentons la méthoderecevoirDegats. Je vous avais expliqué un peu plus haut ce qu'il fallait faire. Vous allez voir, c'est très simple :

void Personnage::recevoirDegats(int nbDegats)
{
    m_vie -= nbDegats;
    //On enlève le nombre de dégâts reçus à la vie du personnage
    
    if (m_vie < 0) //Pour éviter d'avoir une vie négative
    {
        m_vie = 0; //On met la vie à 0 (cela veut dire mort)
    }
}

La méthode modifie donc la valeur de la vie. La méthode a le droit de modifier l'attribut, car elle fait partie de la classe. Ne soyez donc pas surpris : c'est justement l'endroit où on a le droit de toucher aux attributs.

La vie est diminuée du nombre de dégâts reçus. En théorie, on aurait pu se contenter de la première instruction mais on fait une vérification supplémentaire. Si la vie est descendue en-dessous de 0 (parce que le personnage a reçu 20 de dégâts alors qu'il ne lui restait plus que 10 de vie, par exemple), on ramène la vie à 0 pour éviter d'avoir une vie négative (cela ne fait pas très pro, une vie négative). De toute façon, à 0 de vie, le personnage est considéré comme mort.

Et voilà pour la première méthode ! Allez, on enchaîne !

Personnage::attaquer
void Personnage::attaquer(Personnage &cible)
{
    cible.recevoirDegats(m_degatsArme);
    //On inflige à la cible les dégâts que cause notre arme
}

Cette méthode est peut-être très courante, elle n'en est pas moins très intéressante !
On reçoit en paramètre une référence vers un objet de typePersonnage. On aurait pu recevoir aussi un pointeur mais, comme les références sont plus faciles à manipuler, on ne va pas s'en priver.

La référence concerne le personnage cible que l'on doit attaquer. Pour infliger des dégâts à la cible, on appelle sa méthoderecevoirDegatsen faisant :cible.recevoirDegats

Quelle quantité de dégâts envoyer à la cible ? Vous avez la réponse sous vos yeux : le nombre de points de dégâts indiqués par l'attributm_degatsArme! On envoie donc à la cible la valeur dem_degatsArmede notre personnage.

Personnage::boirePotionDeVie
void Personnage::boirePotionDeVie(int quantitePotion)
{
    m_vie += quantitePotion;

    if (m_vie > 100) //Interdiction de dépasser 100 de vie
    {
        m_vie = 100;
    }
}

Le personnage reprend autant de vie que ce que permet de récupérer la potion qu'il boit. On vérifie toutefois qu'il ne dépasse pas les 100 de vie car, comme on l'a dit plus tôt, il est interdit de dépasser cette valeur.

Personnage::changerArme
void Personnage::changerArme(string nomNouvelleArme, int degatsNouvelleArme)
{
    m_nomArme = nomNouvelleArme;
    m_degatsArme = degatsNouvelleArme;
}

Pour changer d'arme, on stocke dans nos attributs le nom de la nouvelle arme ainsi que ses nouveaux dégâts. Les instructions sont très simples : on fait simplement passer dans nos attributs ce qu'on a reçu en paramètres.

Personnage::estVivant
bool Personnage::estVivant()
{
    if (m_vie > 0) //Plus de 0 de vie ?
    {
        return true; //VRAI, il est vivant !
    }
    else
    {
        return false; //FAUX, il n'est plus vivant !
    }
}

Cette méthode permet de vérifier que le personnage est toujours vivant. Elle renvoie vrai (true) s'il a plus de 0 de vie et faux (false) sinon.
On peut, cependant, faire plus court en renvoyant directement la valeur du test.

bool Personnage::estVivant()
{
    return m_vie > 0; //Renvoie true si m_vie > 0 et false sinon.
}

C'est plus concis et aussi beaucoup plus clair. Vous n'êtes peut-être pas encore habitué aux tests booléens, mais c'est la bonne manière d'écrire ce genre de fonctions. Les programmeurs ne veulent pas s'embêter avec des parenthèses inutiles.

Code complet dePersonnage.cpp

En résumé, voici le code complet dePersonnage.cpp:

#include "Personnage.h"

using namespace std;

void Personnage::recevoirDegats(int nbDegats)
{
    m_vie -= nbDegats;
    //On enlève le nombre de dégâts reçus à la vie du personnage

    if (m_vie < 0) //Pour éviter d'avoir une vie négative
    {
        m_vie = 0; //On met la vie à 0 (cela veut dire mort)
    }
}

void Personnage::attaquer(Personnage &cible)
{
    cible.recevoirDegats(m_degatsArme);
    //On inflige à la cible les dégâts que cause notre arme
}

void Personnage::boirePotionDeVie(int quantitePotion)
{
    m_vie += quantitePotion;

    if (m_vie > 100) //Interdiction de dépasser 100 de vie
    {
        m_vie = 100;
    }
}

void Personnage::changerArme(string nomNouvelleArme, int degatsNouvelleArme)
{
    m_nomArme = nomNouvelleArme;
    m_degatsArme = degatsNouvelleArme;
}

bool Personnage::estVivant()
{
    return m_vie > 0;
}

main.cpp

Retour au main(). Première chose à ne pas oublier : inclure Personnage.hpour pouvoir créer des objets de typePersonnage.

#include "Personnage.h" //Ne pas oublier

Le main()reste le même que tout à l'heure, on n'a pas besoin de le modifier. Au final, le code est donc très court et le fichier main.cppne fait qu'utiliser les objets :

#include <iostream>
#include "Personnage.h" //Ne pas oublier

using namespace std;

int main()
{
    Personnage david, goliath;
    //Création de 2 objets de type Personnage : david et goliath

    goliath.attaquer(david); //goliath attaque david
    david.boirePotionDeVie(20); //david récupère 20 de vie en buvant une potion
    goliath.attaquer(david); //goliath réattaque david
    david.attaquer(goliath); //david contre-attaque... c'est assez clair non ? 
    goliath.changerArme("Double hache tranchante vénéneuse de la mort", 40);
    goliath.attaquer(david);

    return 0;
}

Pour le moment il faudra donc vous contenter de votre imagination. Essayez d'imaginer que David et Goliath sont bien en train de combattre (je ne veux pas vous gâcher la chute mais, normalement, c'est David qui gagne à la fin) !

En résumé

  • Il est nécessaire de créer une classe pour pouvoir ensuite créer des objets.

  • La classe est le plan de construction de l'objet.

  • Une classe est constituée d'attributs et de méthodes (variables et fonctions).

  • Les éléments qui constituent la classe peuvent être publics ou privés. S'ils sont publics, tout le monde peut les utiliser n'importe où dans le code. S'ils sont privés, seule la classe peut les utiliser.

  • En programmation orientée objet, on suit la règle d'encapsulation : on rend les attributs privés, afin d'obliger les autres développeurs à utiliser uniquement les méthodes.

Exemple de certificat de réussite
Exemple de certificat de réussite