Jeux Libres
       
           

» Les Tutoriels » Apprenez à programmer en C++ ! » Les classes (Partie 1/2)

Note : Vous vous apprêtez à lire un tutoriel de M@teo21 et Nanoc initialement publié à cette adresse sous la licence Creative Commons BY-NC-SA 2.0.

Les classes (Partie 1/2)


Dans le 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. Ca 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.

A partir de maintenant, nous allons apprendre à créer des objets. Vous allez voir que c'est tout un art et que ça 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. Pas évident. Il faudra donc bien réfléchir avant de se lancer dans le code comme des forcenés. :p


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





Chapitre précédent     Sommaire     Chapitre suivant


Créer une classe


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

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 les plans. Pour les objets c'est pareil : une fois que vous avez fait la classe (le plan), vous pourrez créer autant d'objets du même type que vous voulez. :)

Vocabulaire : on dit qu'un objet est une instance d'une classe. C'est un mot très courant que l'on rencontre souvent en POO. Cela signifie qu'un objet est la matérialisation concrète d'une classe (tout comme la maison est la matérialisation concrète du plan de la maison).
Oui je sais c'est très métaphysique la POO, mais vous allez voir on s'y fait. ^^



Créer une classe, oui mais laquelle ?



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

Pour reprendre mon exemple sur l'architecture : allons-nous créer un appartement, une villa avec piscine, un spacieux loft ?
En clair, quel type d'objet voulons-nous être capable 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 avez pas trop le choix de toute façon :p ).
Pour notre exemple, nous allons créer une classe Personnage qui va permettre de représenter un personnage de jeu de rôle (RPG).

Si vous n'avez pas l'habitude des jeux de rôle, rassurez-vous, moi non plus. Vous n'avez pas besoin de savoir jouer à des RPG pour suivre ce chapitre. J'ai choisi cet exemple car il me paraît didactique, amusant, et qu'il peut déboucher sur la création d'un jeu à la fin. ;)


Bon, on la crée cette classe ?



C'est parti. :)

Pour commencer, je vous rappelle qu'une classe est constituée :

  • 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)

(n'oubliez pas ce vocabulaire, il est fon-da-men-tal !)

Voici le code minimal pour créer une classe :
1
2
3
4
class Personnage
{
   
}; // N'oubliez pas le point-virgule la fin !

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

Souvenez-vous de cette règle très importante : il faut que le nom de vos classes commence toujours par une lettre majuscule ! Bien que ce ne soit pas obligatoire (le compilateur ne hurlera pas si vous commencez par une minuscule), cela vous sera très utile par la suite pour différencier les noms des classes des noms des objets.


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, super 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 Personnage est 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 notre Personnage. De quels attributs et de quelles méthodes a-t-il besoin ? Ca, c'est justement l'étape de réflexion, la plus importante. C'est pour ça 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.

Cette étape de réflexion avant le codage est essentielle quand on fait de la POO. Beaucoup de gens, dont moi, ont l'habitude de sortir une feuille de papier et un crayon pour arriver à établir la liste des attributs et méthodes dont ils vont avoir besoin.
Un langage spécial appelé UML a d'ailleurs été spécialement conçu pour "dessiner" les classes avant de commencer à les coder.


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, ça fait un premier attribut : vie ! On dira que ce sera un int, et qu'il sera compris entre 0 et 100 (0 = mort, 100 = toute la vie).
  • Dans un jeu de rôle (RPG), il y a le niveau de magie, aussi appelé mana. Là encore, on va dire que c'est un int compris entre 0 et 100. Si le personnage a 0 de mana, il ne peut plus lancer de sorts magiques et doit attendre que sa mana se recharge toute seule 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 un string pour stocker le nom de l'arme.
  • Enfin, il me semble indispensable d'ajouter un attribut degatsArme, un int toujours, qui indiquerait cette fois le nombre de dégâts que fait notre arme à chaque coup.

On peut donc déjà commencer à compléter notre classe avec ces premiers attributs :
1
2
3
4
5
6
7
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 puisque nous utilisons des chaînes de caractères.

La chose essentielle à retenir ici, est que l'on utilise des attributs pour représenter la notion d'appartenance. On dit qu'un Personnage A 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 bonne solution à adopter. ;)

Les méthodes



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

Voici quelques actions qu'on peut faire avec notre personnage :

  • recevoirDegats : le personnage prend un certain nombre de dégâts, donc perd de la vie.
  •  attaquer : le personnage attaque un autre personnage avec son arme. Il fait autant de dégâts que son arme lui permet d'en faire (c'est-à-dire degatsArme).
  • 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 : renvoie vrai si le personnage est toujours vivant (points de vie supérieur à 0), renvoie faux sinon.

Voilà c'est un bon début je trouve. :)

On va rajouter ça dans la classe avant les attributs (on préfère présenter les méthodes avant les attributs en POO, bien que ça ne soit pas obligatoire) :
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
class Personnage
{
   // Mthodes
   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;    
};

Je n'ai pas écrit le code des méthodes exprès, on le fera après. ;)


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

Par exemple, recevoirDegats retranchera le nombre de dégâts indiqués en paramètre par nbDegats à la vie du personnage.
Intéressante aussi : la méthode attaquer. 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éthode recevoirDegats de la cible pour lui infliger des dégâts. :D

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

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

En résumé : comme je vous l'avais dit, 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 qui est capable de surveiller son bon fonctionnement tout seul.


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 ça signifie.

Tout d'abord un petit rappel. En POO, il y a 2 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 dans le chapitre précédent.

Il faut bien distinguer ces 2 parties, car ça devient ici très important.

Je mets un exemple création / utilisation côte à côte pour que vous puissiez bien les différencier :

Création de la classeUtilisation de l'objet
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
class Personnage
{
   void recevoirDegats(int nbDegats)
   {
 
   }
 
   void attaquer(Personnage &cible)
   {
 
   }
 
   void boirePotionDeVie(int quantitePotion)
   {
 
   }
 
   void changerArme(string nomNouvelleArme, int degatsNouvelleArme)
   {
 
   }
 
   bool estVivant()
   {
 
   }
 
   int m_vie;
   int m_mana;
   string m_nomArme;
   int m_degatsArme;
 
};

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int main()
{
   Personnage david, goliath;
 
   goliath.attaquer(david);
   david.boirePotionDeVie(20);
   goliath.attaquer(david);
   david.attaquer(goliath);
   goliath.changerArme("Double hache tranchante vnneuse de la mort", 40);
   goliath.attaquer(david);
 
 
   return 0;
}

Tenez, pourquoi on n'essaierait pas ce code ?
Allez, on met tout dans un même fichier (en prenant soin de définir la classe avant le main), et zou !
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 <iostream>
#include <string>
 
using namespace std;
 
class Personnage
{
   // Mthodes
   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; // Cration de 2 objets de type Personnage : david et goliath
 
   goliath.attaquer(david); // goliath attaque david
   david.boirePotionDeVie(20); // david boit une potion de vie qui lui rapporte 20 de vie
   goliath.attaquer(david); // goliath rattaque david
   david.attaquer(goliath); // david contre-attaque... c'est assez clair non ? ^^
   goliath.changerArme("Double hache tranchante vnneuse de la mort", 40);
   goliath.attaquer(david);
 
 
   return 0;
}

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

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

Encore une nouvelle 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 (eh ouais j'ai fait exprès de provoquer cette erreur de compilation, vous aviez quand même pas cru que j'avais pas tout 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 2 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 sont private.

Il existe d'autres droits d'accès mais ils sont un peu plus complexes. Nous les verrons plus tard.


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

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

Voici ce que je vous propose de faire : on va mettre en public toutes les méthodes, et en privé tous les attributs.
Ca donne ça :
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
class Personnage
{
   // Tout ce qui suit est public (accessible depuis l'extrieur)
   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'extrieur)
   private:
   
   int m_vie;
   int m_mana;
   string m_nomArme;
   int m_degatsArme;
};

Tout ce qui suit le 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 :p ). On appelle des méthodes depuis le main : comme elles sont publiques, on a le droit de le faire.
... par contre, 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 :
1
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... ça veut dire qu'on ne peut pas modifier la vie du personnage depuis le main ? Eh oui !
C'est nul ? Non au contraire, c'est très bien pensé, ça s'appelle l'encapsulation. ;)

L'encapsulation



Moi j'ai une solution ! Si on mettait tout en public ? Les méthodes ET les attributs en public, comme ça on peut tout modifier depuis le main et plus aucun problème !
... quoi j'ai dit une connerie ?


Oh, trois fois rien, vous venez juste de vous faire autant d'ennemis qu'il n'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

Ca 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à.
En clair, si j'en vois un à partir de maintenant qui me met ne serait-ce qu'un seul attribut en public, je le brûle, je le torture, je l'écorche vif sur la place publique, compris ?
Et vous, si vous voyez quelqu'un d'autre faire ça un jour, écorchez-le vif en pensant à moi, vous serez sympa.

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

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


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 c'est justement de masquer les informations complexes à l'utilisateur (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 ça 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 permet de faire en sorte que le contenu de l'objet reste une "boîte noire". On ne sait pas comment ça fonctionne à l'intérieur quand on l'utilise, et c'est très bien. C'est une sécurité, ça permet d'éviter de faire péter tout le bazar de fioles chimiques à l'intérieur. :-°


Séparer 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 2 fichiers différents pour avoir un code plus modulaire.
  • Implémenter les méthodes de notre classe Personnage (c'est-à-dire écrire le code à l'intérieur parce que pour le moment y'a rien :-° ).

Pour le moment, on a mis notre classe dans le fichier main.cpp, juste au-dessus du main. Et les méthodes sont directement écrites dans la définition de la classe. Ca fonctionne, mais c'est un peu bourrin.

Pour améliorer cela, tout d'abord il faut clairement séparer le main (qui se trouve dans main.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 leurs implémentations

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

  • Personnage.h
  • Personnage.cpp

(vous noterez que je mets aussi une majuscule à la première lettre du nom de fichier, histoire d'être cohérent jusqu'au bout)

Vous devriez être capables de faire ça tous seuls avec votre IDE favori. Sous Code::Blocks, je fais File / New File, je rentre par exemple le nom "Personnage.h" avec l'extension, et je réponds "Oui" quand Code::Blocks me demande si je veux ajouter le nouveau fichier au projet en cours :


Personnage.h



Le fichier .h va donc contenir la déclaration de la classe avec les attributs et les prototypes des méthodes. Dans notre cas, pour la classe Personnage, ça va donner ça :
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
#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, donc il faut mettre std:: devant string.
   int m_degatsArme;
};
 
#endif

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

Dans les .h, il est recommandé de ne jamais mettre la directive using namespace std; car cela pourrait avoir des effets néfastes lorsque vous utiliserez la classe par la suite.
Par conséquent, il faut rajouter le préfixe "std::" devant chaque string du .h. Sinon, le compilateur vous sortira une erreur du type "string does not name a type".


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 ça va pas bien se passer, c'est d'inclure <string> et "Personnage.h".
On peut aussi rajouter ici un using namespace std;. On a le droit de le faire car on est dans le .cpp (par contre comme je vous l'ai expliqué plus tôt, il faut éviter de le mettre dans le .h).
1
2
3
#include "Personnage.h"
 
using namespace std;

Maintenant, voilà comment ça 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 "::". Pour recevoirDegats ça donne ça :
1
2
3
4
void Personnage::recevoirDegats(int nbDegats)
{
 
}

Cela permet au compilateur de savoir que cette méthode se rapporte à la classe Personnage. 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éthode recevoirDegats. Je vous avais expliqué un peu plus haut ce qu'il fallait faire. Vous allez voir, c'est très simple :
1
2
3
4
5
6
7
8
9
void Personnage::recevoirDegats(int nbDegats)
{
   m_vie -= nbDegats; // On enlve le nombre de dgts reus la vie du personnage
   
   if (m_vie < 0) // Pour viter d'avoir une vie ngative
   {
       m_vie = 0; // On met la vie 0 (a 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 qu'on a reçu 20 de dégâts alors qu'on n'avait que 10 de vie), on ramène la vie à 0 afin d'éviter d'avoir une vie négative (ça 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 hop hop hop !

Personnage::attaquer


1
2
3
4
void Personnage::attaquer(Personnage &cible)
{
   cible.recevoirDegats(m_degatsArme); // On inflige la cible les dgts que causent 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 type Personnage. On aurait pu recevoir un pointeur aussi, mais comme les références sont plus faciles à manipuler (cf les chapitres précédents) 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éthode recevoirDegats en 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'attribut m_degatsArme ! On envoie donc la valeur des m_degatsArme de notre personnage à la cible.

Personnage::boirePotionDeVie


1
2
3
4
5
6
7
8
9
void Personnage::boirePotionDeVie(int quantitePotion)
{
   m_vie += quantitePotion;
 
   if (m_vie > 100) // Interdiction de dpasser 100 de vie
   {
       m_vie = 100;
   }
}

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

Personnage::changerArme


1
2
3
4
5
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 juste passer ce qu'on a reçu en paramètres dans nos attributs.

Personnage::estVivant


1
2
3
4
5
6
7
8
9
10
11
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 si le personnage est toujours vivant. Elle renvoie vrai (true) s'il a plus de 0 de vie, et faux (false) sinon.

Code complet de Personnage.cpp



En résumé, le code complet de notre Personnage.cpp est le suivant :
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
#include "Personnage.h"
 
using namespace std;
 
void Personnage::recevoirDegats(int nbDegats)
{
   m_vie -= nbDegats; // On enlve le nombre de dgts reus la vie du personnage
 
   if (m_vie < 0) // Pour viter d'avoir une vie ngative
   {
       m_vie = 0; // On met la vie 0 (a veut dire mort)
   }
}
 
void Personnage::attaquer(Personnage &cible)
{
   cible.recevoirDegats(m_degatsArme); // On inflige la cible les dgts que causent notre arme
}
 
void Personnage::boirePotionDeVie(int quantitePotion)
{
   m_vie += quantitePotion;
 
   if (m_vie > 100) // Interdiction de dpasser 100 de vie
   {
       m_vie = 100;
   }
}
 
void Personnage::changerArme(string nomNouvelleArme, int degatsNouvelleArme)
{
   m_nomArme = nomNouvelleArme;
   m_degatsArme = degatsNouvelleArme;
}
 
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 !
   }
}

main.cpp



Retour au main. Première chose à ne pas oublier : inclure Personnage.h pour pouvoir créer des objets de type Personnage.
1
#include "Personnage.h" // Ne pas oublier

Après, le main reste le même que tout à l'heure, on n'a pas besoin de le changer. Au final, le code du main est donc très court, et le fichier main.cpp ne fait qu'utiliser les objets :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <iostream>
#include "Personnage.h" // Ne pas oublier
 
using namespace std;
 
 
int main()
{
   Personnage david, goliath; // Cration de 2 objets de type Personnage : david et goliath
 
   goliath.attaquer(david); // goliath attaque david
   david.boirePotionDeVie(20); // david boit une potion de vie qui lui rapporte 20 de vie
   goliath.attaquer(david); // goliath rattaque david
   david.attaquer(goliath); // david contre-attaque... c'est assez clair non ? ^^
   goliath.changerArme("Double hache tranchante vnneuse de la mort", 40);
   goliath.attaquer(david);
 
 
   return 0;
}

N'exécutez pas le programme pour le moment. En effet, nous n'avons toujours pas vu comment faire pour initialiser les attributs, ce qui fait que notre programme n'est pas encore utilisable.
Nous verrons comment le rendre pleinement fonctionnel dans le chapitre suivant, et vous pourrez alors (enfin) l'exécuter. ;)


Il faudra donc pour le moment vous contenter de votre imagination. Essayez d'imaginer que David et Goliath sont bien en train de combattre ! (et je veux pas faire mon gros spoiler, mais normalement c'est David qui gagne à la fin :-° ).




Là, on peut dire qu'on est rentré en plein dans la POO. ^^
Pourtant, ce n'est encore qu'un début ! De nombreuses nouvelles choses complètement dingues vous attendent dans les chapitres qui suivent (et elles vont vous rendre dingues ça c'est sûr :-° ).

Un conseil si je puis me permettre : assurez-vous d'avoir bien compris qu'il y avait deux faces dans la POO, la création de la classe, et l'utilisation des objets. Il faut être à l'aise avec ce concept.
Mais tout n'est pas si simple. Comme vous le verrez, ce que font les objets la plupart du temps c'est... utiliser d'autres objets ! Et c'est en combinant plusieurs objets entre eux que l'on découvrira le vrai pouvoir de la POO ;)



Chapitre précédent     Sommaire     Chapitre suivant



Distribué et adapté par David
Consulté 6520 fois



Hébergeur du site : David
Version PHP : 5.4.45-0+deb7u2
Uptime : 300 jours 3 heures 16 minutes
Espace libre : 1519 Mo
Dernière sauvegarde : 19/09/2019
Taille de la sauvegarde : 1115 Mo


5508027 pages ont été consultées sur le site !
Dont 4500 pages pendant les 24 dernières heures.

Page générée en 1.42 secondes


Nos sites préférés
- Création d'un jeu de plateforme de A à Z avec SDL
- Zelda ROTH : Jeux amateurs sur le thème de Zelda
- Zeste de Savoir : la connaissance pour tous et sans pépins
- YunoHost : s'héberger soi-même en toute simplicité
- Site de Fvirtman : recueil de projets et de codes en C et C++
- Par ici la sortie : le site des idées de sorties


  © 2005-2019 linor.fr - Toute reproduction totale ou partielle du contenu de ce site est strictement interdite.