0% ont trouvé ce document utile (0 vote)
219 vues12 pages

Maîtrisez l'encapsulation en C++

Ce document explique les concepts d'encapsulation et de droits d'accès en programmation orientée objet. Il présente comment définir les attributs et méthodes d'une classe comme public ou privé, et l'importance du principe d'encapsulation qui veut que tous les attributs soient privés.

Transféré par

Josias Ndjiki
Copyright
© © All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd
0% ont trouvé ce document utile (0 vote)
219 vues12 pages

Maîtrisez l'encapsulation en C++

Ce document explique les concepts d'encapsulation et de droits d'accès en programmation orientée objet. Il présente comment définir les attributs et méthodes d'une classe comme public ou privé, et l'importance du principe d'encapsulation qui veut que tous les attributs soient privés.

Transféré par

Josias Ndjiki
Copyright
© © All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd

JN

Accueil
> Cours
> Programmez en orienté objet avec C++
> Gérez l'accès et l'encapsulation

Programmez en orienté objet avec C++

10 heures  Difficile

Mis à jour le 10/02/2022

 

Gérez l'accès et l'encapsulation

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

1. On crée des classes pour définir le fonctionnement des objets. C'est ce qu'on fait dans ce


chapitre.
2. On utilise des objets. C'est ce qu'on a fait au chapitre précédent.

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

1. Création de la classe :
cpp

1 class Personnage

2 {

3 // Méthodes

4 void recevoirDegats(int nbDegats)

5 {

7 }

9 void attaquer(Personnage &cible)

10 {

11

12 }

13

14 void boirePotionDeVie(int quantitePotion)

15 {

16

17 }

18

19 void changerArme(string nomNouvelleArme, int degatsNouvelleArme)

20 {

21

22 }

23

24 bool estVivant()

25 {

26

27 }

28

29 // Attributs

30 int m_vie;

31 int m_mana;

32 string m_nomArme;

33 int m_degatsArme;

34 };

2. Utilisation de l'objet :
c_cpp

1 int main()

2 {

3 Personnage david, goliath;

4 //Création de 2 objets de type Personnage : david et goliath

6 goliath.attaquer(david); //goliath attaque david

7 david.boirePotionDeVie(20); //david récupère 20 de vie en buvant une potion

8 goliath.attaquer(david); //goliath attaque david

9 david.attaquer(goliath); //david contre-attaque... c'est assez clair non ?

10

11 goliath.changerArme("Double hache tranchante veneneuse de la mort", 40);

12 goliath.attaquer(david);

13

14

15 return 0;

16 }

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 le  main()  :
c_cpp

1 #include <iostream>
2 #include <string>

4 using namespace std;

6 class Personnage

7 {

8 // Méthodes

9 void recevoirDegats(int nbDegats)

10 {

11

12 }

13

14 void attaquer(Personnage &cible)

15 {

16

17 }

18

19 void boirePotionDeVie(int quantitePotion)

20 {

21

22 }

23

24 void changerArme(string nomNouvelleArme, int degatsNouvelleArme)

25 {

26

27 }

28

29 bool estVivant()

30 {

31

32 }

33

34 // Attributs

35 int m_vie;

36 int m_mana;

37 string m_nomArme;

38 int m_degatsArme;

39 };

40

41 int main()

42 {

43 Personnage david, goliath;

44 //Création de 2 objets de type Personnage : david et goliath

45

46 goliath.attaquer(david); //goliath attaque david

47 david.boirePotionDeVie(20); //david récupère 20 de vie en buvant une potion

48 goliath.attaquer(david); //goliath attaque david

49 david.attaquer(goliath); //david contre-attaque... c'est assez clair non ?

50

51 goliath.changerArme("Double hache tranchante veneneuse de la mort", 40);

52 goliath.attaquer(david);

53

54

55 return 0;

56 }

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 !

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 ?).

Gérez les droits d'accès


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 :

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

2. 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, plus complexes. Nous les verrons plus tard.

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 le  main()   , 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 le  main()   !

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

On va mettre en public toutes les méthodes, et en privé tous les attributs :


c_cpp

1 class Personnage

2 {

3 // Tout ce qui suit est public (accessible depuis l'extérieur)

4 public:

6 void recevoirDegats(int nbDegats)

7 {

9 }

10

11 void attaquer(Personnage &cible)

12 {

13

14 }

15

16 void boirePotionDeVie(int quantitePotion)

17 {

18

19 }

20

21 void changerArme(string nomNouvelleArme, int degatsNouvelleArme)

22 {

23

24 }

25

26 bool estVivant()

27 {

28

29 }

30

31 // Tout ce qui suit est prive (inaccessible depuis l'extérieur)

32 private:

33

34 int m_vie;

35 int m_mana;

36 string m_nomArme;

37 int m_degatsArme;

38 };

Tout ce qui suit le mot-clé  public:   est public, donc toutes nos méthodes sont publiques.

Tout ce qui suit le mot-clé  private:  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()   :

cpp

1 goliath.m_vie = 90;

Essayez, vous verrez que le compilateur vous ressort la même erreur que tout à l'heure.

Mais alors, on ne peut pas modifier la vie du personnage depuis le  main()   ?

C'est ce qu'on appelle l'encapsulation.

Respectez le principe d'encapsulation


Si on mettait tout en public ? Les méthodes et les attributs, comme cela on peut tout modifier
depuis le  main()   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.

Je ne veux pas en voir un seul mettre un attribut en  public   !

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 la métaphore du cube pour un objet ?

Le code à l'intérieur, 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.

Si vous avez fait du C, vous connaissez le mot-clé  struct   . On peut aussi l'utiliser en C++
pour créer des classes.

La seule différence avec le mot-clé  class  est que, par défaut, les méthodes et attributs sont
publics au lieu de privés.

Séparez les prototypes et les définitions


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

1. Séparer les méthodes en prototypes et définitions dans deux fichiers différents, pour avoir un
code plus modulaire.

2. Implémenter les méthodes de la classe  Personnage   (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 fichier  main.cpp   , juste au-dessus du  main()   . 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 le  main()   (qui se trouve dans 

main.cpp   ) des classes.

Pour chaque classe, on va créer :

1. Un "header" (fichier  *.hpp   ) qui contiendra les attributs et les prototypes de la classe.

2. 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 :

1. Personnage.hpp   .

2. Personnage.cpp   .

Vous noterez que je mets aussi une majuscule à la première lettre du nom du fichier, histoire
d'être cohérent jusqu'au bout.
Vous devriez être capable de faire cela tout seul avec votre IDE. Sous Code::Blocks, il faut passer
par les menus  File   >  New File   ; je saisis par exemple le nom  Personnage.hpp  avec
son extension.

1. Le fichier   Personnage.hpp

Le fichier  .hpp  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   , nous obtenons :

c_cpp

1 #ifndef DEF_PERSONNAGE

2 #define DEF_PERSONNAGE

4 #include <string>

6 class Personnage

7 {

8 public:

10 void recevoirDegats(int nbDegats);

11 void attaquer(Personnage &cible);

12 void boirePotionDeVie(int quantitePotion);

13 void changerArme(std::string nomNouvelleArme, int degatsNouvelleArme);

14 bool estVivant();

15

16 private:

17

18 int m_vie;

19 int m_mana;

20 std::string m_nomArme; //Pas de using namespace std, il faut donc mettre std::
devant string

21 int m_degatsArme;

22 };

23

24 #endif

Seuls les prototypes des méthodes figurent dans le  .hpp   . C'est déjà beaucoup plus clair.

Dans les  .hpp   , il est recommandé de ne jamais mettre la directive 

using namespace std;   , car cela pourrait avoir des effets néfastes, par la suite, lorsque
vous utiliserez la classe.

Par conséquent, il faut rajouter le préfixe  std::   devant chaque  string  du  .hpp   .

Sinon, le compilateur vous sortira une erreur du type  string does not name a type   .

2. Le fichier   Personnage.cpp
C'est là qu'on va écrire le code de nos méthodes (on dit qu'on implémente ou qu'on définit les
méthodes).

Nous allons voir dans ce screencast comment définir l’ensemble des méthodes de la classe en
respectant toutes les bonnes pratiques de développement. Vous êtes prêt ?

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

c_cpp

1 #include "Personnage.hpp"

3 using namespace std;

5 void Personnage::recevoirDegats(int nbDegats)

6 {

7 m_vie -= nbDegats;

8 //On enlève le nombre de dégâts reçus à la vie du personnage

10 if (m_vie < 0) //Pour éviter d'avoir une vie négative

11 {

12 m_vie = 0; //On met la vie à 0 (cela veut dire mort)

13 }

14 }

15

16 void Personnage::attaquer(Personnage &cible)

17 {

18 cible.recevoirDegats(m_degatsArme);

19 //On inflige à la cible les dégâts que cause notre arme

20 }

21

22 void Personnage::boirePotionDeVie(int quantitePotion)

23 {

24 m_vie += quantitePotion;

25

26 if (m_vie > 100) //Interdiction de dépasser 100 de vie

27 {

28 m_vie = 100;

29 }

30 }

31

32 void Personnage::changerArme(string nomNouvelleArme, int degatsNouvelleArme)

33 {

34 m_nomArme = nomNouvelleArme;

35 m_degatsArme = degatsNouvelleArme;

36 }

37

38 bool Personnage::estVivant()

39 {

40 return m_vie > 0;

41 }

3. Le fichier   main.cpp

Retour au  main()   . Première chose à ne pas oublier : inclure  Personnage.hpp   pour pouvoir

créer des objets de type  Personnage   .

c_cpp

1 #include "Personnage.hpp" //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.cpp  ne fait qu'utiliser les objets :

c_cpp

1 #include <iostream>
2 #include "Personnage.hpp" //Ne pas oublier

4 using namespace std;

6 int main()

7 {

8 Personnage david, goliath;

9 //Création de 2 objets de type Personnage : david et goliath

10

11 goliath.attaquer(david); //goliath attaque david

12 david.boirePotionDeVie(20); //david récupère 20 de vie en buvant une potion

13 goliath.attaquer(david); //goliath attaque david

14 david.attaquer(goliath); //david contre-attaque... c'est assez clair non ?

15 goliath.changerArme("Double hache tranchante veneneuse de la mort", 40);

16 goliath.attaquer(david);

17

18 return 0;

19 }

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 rend notre programme inutilisable.

Nous verrons comment le rendre pleinement fonctionnel très bientôt, et vous pourrez alors (enfin
!) l'exécuter.

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é


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.

Pourquoi initialiser un objet ? C’est justement ce qu’on va voir dans le chapitre suivant : les constructeurs
et les destructeurs.

J'ai terminé ce chapitre et je passe au suivant

Et si vous obteniez un diplôme OpenClassrooms ?


Formations jusqu’à 100 % financées

Date de début flexible

Projets professionnalisants

Mentorat individuel

Trouvez la formation et le financement faits pour vous

Être orienté Comparez nos types de formation

 Créez une classe Initialisez un objet 

Les professeurs
Mathieu Nebra
Entrepreneur à plein temps, auteur à plein temps et co-fondateur d'OpenClassrooms :o)
Ranga Gonnage
Développeur logiciel, mentor et enseignant.

OPENCLASSROOMS

OPPORTUNITÉS

AIDE

POUR LES ENTREPRISES

EN PLUS

Français
 

Vous aimerez peut-être aussi