Big Tuto : Apprenez le C++

Chapitre 17 : Introduction aux classes (3)

 

Tutoriel présenté par : Robert Gillard (Gondulzac)
Date de publication : 4 décembre 2016
Date de révision : -

 

Retrouvez les projets complets de ce chapitre :

  

 

 

   1 – Données et méthodes membres statiques

 

   Le terme ''variable statique'' n'est pas une nouveauté pour vous. Nous en avons parlé dans le chapitre 6 en donnant un exemple de compteur dans une fonction (Chap 6 § 1.4 – Domaine de visibilité des variables). wink

   Dans le chapitre 13 (Types de données complexes – 1ère partie), nous avons vu que nous pouvions déclarer une ou plusieurs variables statiques dans une structure et ce, afin d'obtenir des valeurs communes à différents objets issus de cette structure (§ 1.6).

   Dans une classe, nous pouvons également déclarer des variables membres et méthodes statiques pouvant être publiques ou privées mais à la grande différence que si dans dans une structure, une variable statique pouvait être associée à un ou plusieurs objets, les membres statiques d'une classe ne sont associés qu'à la classe elle-même, pas aux objets qui en sont issus. Nous n'accéderons donc pas à la valeur d'un membre statique d'une classe par l'intermédiaire d'un objet de cette classe mais bien à l'aide du nom de la classe elle-même, suivi de l'opérateur de résolution de portée et du nom de la variable statique ou d'une fonction membre déclarée ''static''. Si tout ceci vous semble un peu flou, nous allons le présenter dans un simple exemple. angel

     Nous ne réitérerons pas l'exemple d'un compteur mais nous allons créer une simple classe ''Marchand'' nous permettant uniquement de récupérer son nom. Et afin de mieux marquer la différence de la gestion entre données statiques et données non-statiques, nous ajouterons des fonctions membres d'accès publique que nous connaissons bien. wink

   La variable membre statique devant être initialisée dans le fichier d'implémentation, nous créerons donc un fichier header et deux fichiers .cpp.


   Exemple : Projet 103Class_10

      Fichier 103Class_10.h  

 

//projet 103Class_10
//Fichier : 103Class_10.h
//Déclaration de la classe Marchand
//Utilisation d'une variable et d'une fonction statiques en accès publique
#ifndef DEF_103CLASS_10
#define DEF_103CLASS_10
 
#include <iostream>
#include <string>
 
class Marchand
{
public:
 
//Méthodes membres d'accès publiques non statiques
//Assignation de donnée
void set_Name(std::string name);
//Acquisition de donnée
std::string get_Name() const;
 
//variable et fonction statique en accès public
static std::string autreNom;
static void affiche_Nom();
 
private:
//Donnée membre privée
std::string _nom;
 
};
 
#endif 

 

     Nous déclarons d'une part deux méthodes membres d'accès public set_Name() et get_Name() ainsi qu'une variable privée _nom. Nous savons déjà depuis un certain temps maintenant que nous ne pourrons accéder à la valeur de cette variable privée que par l'intermédiaire de ces méthodes d'accès publique. wink

   Ensuite, dans la partie publique, nous déclarons une variable statique ''autreNom'' ainsi qu'une fonction membre statique ''affiche_Nom()''.


      Fichier 103Class_10.cpp 

 

//projet 103Class_10
//Fichier : 103Class_10.cpp
//Implémentation des méthodes d'accès publique set_Name() et get_Name()
//ainsi que de la fonction statique affiche_Nom()
 
#include "103Class_10.h"
 
 
using namespace std;
 
//Méthode d'accès publique d'assignation (mutateur)
//Initialisation de la variable membre _nom par la variable name passée en paramètre
void Marchand::set_Name(string name) { _nom = name; }
 
//Méthode d'accès publique d'acquisition (accesseur)
//Renvoie le contenu de la variable _nom
string Marchand::get_Name() const { return _nom; }
 
// On définit la variable statique dans le fichier d'implémentation
string Marchand::autreNom = "";
 
//Implémentation de la fonction affiche_nom
void Marchand::affiche_Nom() { cout << autreNom << endl; } 

 

    L'implémentation des fonctions membres d'accès publique est connue, il n'y a rien à ajouter. Par contre, à la suite de celles-ci vous voyez que nous définissons la variable statique ''autreNom''. L'accès à cette variable se fait en écrivant premièrement son type, suivi du nom de la classe à laquelle elle se réfère, de l'opérateur de résolution de portée et du nom de cette variable. smiley

Attention ! L'initialisation de la variable statique est obligatoire, sans quoi le compilateur va se permettre une remarque désobligeante. cheeky Maintenant, peu importe comment la variable est initialisée : avec une valeur quelconque ou sans valeur du tout, l'important étant qu'elle le soit. wink

   Enfin, pour terminer, nous procédons à l'implémentation de la fonction affiche_Nom() qui, comme son nom l'indique, nous donnera la valeur contenue dans la variable statique autreNom.


      Fichier 103Class_10mainFile.cpp

 

//projet 103Class_10
//Fichier : 103Class_10mainFile.cpp
//Utilisation de variables et méthodes statiques
 
#include "103Class_10.h"
#include <conio.h>
 
using namespace std;
 
 
int main()
{
//Création d'un objet de type Marchand
Marchand unMarchand;
 
//Assigation d'un nom à l'objet
unMarchand.set_Name("Zorbar");
cout << endl;
cout << "Acces au nom par la methode d'acces publique get_Name():" << endl;
cout << unMarchand.get_Name() << endl << endl;
 
 
// On accède à la variable autreNom et à la fonction statique affiche_Nom()
// sans passer par un objet
Marchand::autreNom = "Ethiolas";
cout << "Acces au nom a l'aide de la variable statique publique autreNom:" << endl;
 
cout << Marchand::autreNom << endl << endl;
cout << "Acces au nom a l'aide de la fonction statique affiche_Nom():" << endl;
 
Marchand::affiche_Nom();
 
_getch();
return 0;
}

 

    Dans le fichier 103Class_10mainFile.cpp on crée une instance de la classe Marchand. Nous initialisons cet objet à l'aide d'une fonction publique d'assignation et nous lui donnons ''Zorbar'' pour nom. Nous accédons à ce nom à l'aide de l'accesseur get_Name().

   Nous avons déclaré la variable statique autreNom dans la partie publique de la classe et c'est pourquoi nous pouvons y accéder directement dans la fonction main(). wink

   Ce qui nous donne dans la console :

 

 

   Si nous avions déclaré la variable statique dans la partie privée de la classe nous n'aurions pu y avoir accès que par l'intermédiaire de la fonction affiche_Nom() dans la fonction main().

   Les différences entre ces deux exemples étant minimes, je ne présenterai pas ce dernier ici. J'ai simplement créé un projet 104Class_11 que vous pourrez télécharger avec ce chapitre si cela vous intéresse. wink

 

   Exercice 1 :

   Créez une classe ''Guerrier_des_Plaines'' et une instance de classe. Dans la fonction main() vous comptabiliserez 10 guerriers à l'aide d'un compteur.

   Le compteur devra se faire de deux manières :

1) En utilisant une fonction membre d'accès publique et une variable privée.
2) En utilisant une fonction et une variable déclarées ''public'' suivant le schéma de l'exemple 103Class_10 ci-dessus.

 


   2 – Données membres mutables (C++ 11)

 

   Il se peut, qu'à l'intérieur d'une classe, nous ayions besoin de modifier la valeur d'une variable et ceci, même à l'intérieur d'une fonction membre déclarée ''const''. Une telle variable sera précédée du mot-clé ''mutable'' dans sa déclaration mais ne pourra en aucun cas être une variable const même s'il s'agit d'un membre d'un objet const.

   Nous garderons en mémoire que n'importe quelle fonction membre const peut modifier la valeur d'une donnée membre déclarée ''mutable''.

   Et pour illustrer notre propos nous allons modifier le projet 103Class_10 pour afficher le nombre de fois auquel nous aurons accédé à la fonction affiche_nom().


   Exemple : Projet 105Class_12

      Fichier 105Class_12.h   

 

//projet 105Class_12
//Fichier : 105Class_12.h
//Déclaration de la classe Marchand
//Utilisation d'une variable mutable
#ifndef DEF_105CLASS_12
#define DEF_105CLASS_12
 
#include <iostream>
#include <string>
 
using namespace std;
 
class Marchand
{
public:
 
//Fonctions membres d'accès publiques
//Assignation de donnée
void set_Name(std::string name) { _nom = name; }
 
//Acquisition de donnée
size_t get_Access() const { return _access; }
 
//Fonction lire_Nom()
void lire_Nom() const
{
cout << _nom << endl;
_access++;
}
 
private:
//Donnée membre privée
std::string _nom;
 
mutable size_t _access = 0;
 
};
#endif 

 

   Dans la partie private du fichier header, on initialise à 0 une variable mutable dénommée _access possédant le type size_t.

   Dans la partie publique on définit une fonction membre get_Access() d'accès public retournant la valeur de la variable mutable de type size_t.

   Nous définissons par la suite la fonction lire_Nom() à laquelle est adjoint le spécificateur const.

   Cette fonction affiche le contenu de la variable privée _nom et incrémente la valeur de la variable _access. Et nous avons vu que ceci était rendu possible parce que cette variable est déclarée mutablewink

 

      Fichier 105Class_12mainFile.cpp  

 

//projet 105Class_12
//Fichier : 105Class_12mainFile.cpp
//Utilisation d'une variable mutable
 
#include "105Class_12.h"
#include <conio.h>
 
using namespace std;
 
 
int main()
{
//Création d'un objet de type Marchand
Marchand unMarchand;
 
//Assigation d'un nom à l'objet
unMarchand.set_Name("Ethiolas");
cout << endl;
 
//On appelle 10 fois la fonction lire_Nom()
for (auto i = 0; i < 10; ++i)
unMarchand.lire_Nom();
 
cout << endl;
cout << "Le nom du marchand a ete lu " << unMarchand.get_Access() << " fois" << endl;
 
_getch();
return 0;
} 

 

    Dans la fonction main() nous créons une instance de la classe Marchand. Le nom du marchand est défini à l'aide de la fonction membre d'assignation setName(). On appelle ensuite 10 fois la fonction membre lire_Nom() et pour terminer, la méthode d'accès public get_Access() affiche le nombre de fois que la fonction lire_Nom() a été appelée. wink

 


   3 – Association de classes


   Utilisation d'une fonction amie comme ''pont'' entre deux classes :

   Jusqu'à présent, nos exemples et exercices ne faisaient appel qu'à une seule classe. Nous pouvons cependant associer de nombreuses classes dans un projet et nous allons le voir dans un premier exemple qui fait appel à une fonctionnalité du C++ que l'on appelle l'amitié. Dans cet exemple, nous allons créer une fonction dite ''amie'', commune à deux classes différentes et qui va permettre d'avoir accès à des membres privés de ces classes. wink

   Le but de cet exemple est d'échanger les armes secondaires des inventaires de deux guerriers issus de classes différentes. Nous allons donc devoir accéder aux membres privés de ces classes.


   Exemple : Projet 106Class_13

      Fichier Guerrier_du_Nord.h

 

//projet 106Class_13
//Fichier : Guerrier_du_nord.h
//Déclaration de la classe Guerrier_du_Nord
//Utilisation d'une fonction amie commune comme pont entre deux classes
#ifndef DEF_GUERRIER_NORD
#define DEF_GUERRIER_NORD
 
 
#include <string>
 
//Nous déclarons la classe Guerrier_des_Plaines avant toute chose
//car la classe Guerrier_du_Nord y fait référence à l'intérieur
//de la fonction amie Echange_arme() et ce, afin que le compilateur sache qu'elle existe.
class Guerrier_des_Plaines;
 
class Guerrier_du_Nord //déclare la classe Guerrier_du_Nord
{
public:
 
//constructeurs
Guerrier_du_Nord();
Guerrier_du_Nord(std::string armePrincipale, std::string armeSecondaire);
 
//destructeur
~Guerrier_du_Nord();
 
//méthodes d'accès publiques
std::string ArmePrincipale_GetName() const;
std::string ArmeSecondaire_GetName() const;
void ArmePrincipale_SetName(std::string armePrincipale);
void ArmeSecondaire_SetName(std::string armeSecondaire);
 
//DEFINITION D'UNE FONCTION AMIE EN TANT QUE PONT ENTRE LES
//CLASSES GUERRIER_DU_NORD et GUERRIER_DES_PLAINES
friend void Echange_arme(Guerrier_du_Nord, Guerrier_des_Plaines);
 
private:
 
std::string _armePrincipale;
std::string _armeSecondaire;
};
#endif 

 

   J'ai commenté les fichiers de ce projet un maximum. Gardez à l'esprit que le but de ce projet est d'intervertir deux membres privés de deux classes différentes. wink

   Ce premier fichier déclare la classe Guerrier_du_Nord. On passe deux arguments de type string au constructeur surchargé. Suivent des méthodes d'accès public se rapportant aux armes principales et secondaires de chaque Guerrier.

   On déclare ensuite une fonction amie (friend) Echange_arme() qui agira comme pont entre les deux classes. C'est par l'intermédiaire de cette fonction que l'échange des armes secondaires (qui sont des membres privés, ne l'oubliez pas wink) va se faire, nous allons bientôt voir comment.

   Et comme cette fonction fait référence à la classe Guerrier_des_Plaines à l'intérieur de ses parenthèses, nous devons signaler que cette classe existera en la déclarant juste avant la classe Guerrier_du_Nord.

 

      Fichier Guerrier_des_Plaines.h 

 

//Fichier : Guerrier_des_Plaines.h
//Déclaration de la classe Guerrier_des_Plaines
//Utilisation d'une fonction amie commune comme pont entre deux classes
#ifndef DEF_GUERRIER_PLAINES
#define DEF_GUERRIER_PLAINES
 
#include <string>
 
//Nous déclarons la classe Guerrier_du_Nord avant toute chose
//car la classe Guerrier_des_Plaines y fait référence à l'intérieur
//de la fonction amie Echange_arme() et ce, afin que le compilateur sache qu'elle existe.
 
class Guerrier_du_Nord;
 
class Guerrier_des_Plaines
{
public:
 
//constructeurs
Guerrier_du_Nord();
Guerrier_des_Plaines(std::string armePrincipale, std::string armeSecondaire);
 
//Destructeur
~Guerrier_des_Plaines();
 
//méthodes d'accès publiques
std::string ArmePrincipale_GetName() const;
std::string ArmeSecondaire_GetName() const;
void ArmePrincipale_SetName(std::string armePrincipale);
void ArmeSecondaire_SetName(std::string armeSecondaire);
 
//DEFINITION D'UNE FONCTION AMIE EN TANT QUE PONT ENTRE LES
//CLASSES GUERRIER_DU_NORD et GUERRIER_DES_PLAINES
friend void Echange_arme(Guerrier_du_Nord, Guerrier_des_Plaines);
 
private:
 
std::string _armePrincipale;
std::string _armeSecondaire;
 
};
#endif 

 

    Les commentaires du précédent fichier sont bien entendu valables pour celui-ci. cheeky J'ajouterai simplement qu'il nous faut également déclarer la fonction Echange_arme() comme nous l'avons fait dans la classe Guerrier_du_Nord et que de même, comme nous faisons référence à la classe Guerrier_du_Nord dans cette fonction, nous signalons que cette classe existe en la déclarant juste avant la classe Guerrier_des_Plaines.

 

      Fichier Guerrier_du_Nord.cpp

  

//projet 106Class_13

//Fichier : Guerrier_du_nord.cpp

//Définition de la classe Guerrier_du_Nord

//Utilisation d'une fonction amie commune comme pont entre deux classes

 

 

#include "Guerrier_du_Nord.h"

 

using namespace std;

 

 

//constructeurs de Guerrier_du_Nord

Guerrier_du_Nord::Guerrier_du_Nord() {}

 

Guerrier_du_Nord::Guerrier_du_Nord(string armePrincipale, string armeSecondaire)

{

_armePrincipale = armePrincipale;

_armeSecondaire = armeSecondaire;

}

 

 

//destructeur de Guerrier_du_Nord

Guerrier_du_Nord::~Guerrier_du_Nord() {}

 

//Guerrier_du_Nord::ArmePrincipale_GetName(), méthode d'accès publique

//renvoie la valeur du membre _armePrincipale;

string Guerrier_du_Nord::ArmePrincipale_GetName() const

{

return _armePrincipale;

}

 

 

//Guerrier_du_Nord::ArmeSecondaire_GetName(), méthode d'accès publique

//renvoie la valeur du membre _armeSecondaire;

string Guerrier_du_Nord::ArmeSecondaire_GetName() const

{

return _armeSecondaire;

}

 

 

//définition de Guerrier_du_Nord::ArmePrincipale_SetName(), méthode d'accès publique

//définit le membre _armePrincipale

void Guerrier_du_Nord::ArmePrincipale_SetName(string armePrincipale)

{

//initialise la variable membre _armePrincipale

//avec la valeur passée par le paramètre armePrincipale

_armePrincipale = armePrincipale;

}

 

//définition de Guerrier_du_Nord::ArmeSecondaire_SetName(), méthode d'accès publique

//définit le membre _armeSecondaire

void Guerrier_du_Nord::ArmeSecondaire_SetName(string armeSecondaire)

{

//initialise la variable membre _armeSecondaire

//avec la valeur passée par le paramètre armeSecondaire

_armeSecondaire = armeSecondaire;

} 

 

   Le fichier Guerrier_du_Nord.cpp implémente les constructeurs et les fonctions membres d'accès publique. Il n'y a rien ici que vous ne connaissiez déjà. Juste une remarque, la fonction Echange_arme(), commune aux deux classes, sera implémentée dans un fichier séparé dénommé ''Pont'' et que nous verrons à la fin de notre projet. wink

 

      Fichier Guerrier_des_Plaines.cpp 

 

//projet 106Class_13
//Fichier : Guerrier_des_Plaines.cpp
 
//Définition de la classe Guerrier_des_Plaines
//Utilisation d'une fonction amie commune comme pont entre deux classes
 
#include "Guerrier_des_Plaines.h"
 
 
using namespace std;
 
//constructeurs de Guerrier_des_Plaines
Guerrier_des_Plaines::Guerrier_des_Plaines() {}
 
Guerrier_des_Plaines::Guerrier_des_Plaines(string armePrincipale, string armeSecondaire)
{
_armePrincipale = armePrincipale;
_armeSecondaire = armeSecondaire;
}
 
//destructeur de Guerrier_des_Plaines
Guerrier_des_Plaines::~Guerrier_des_Plaines() {}
 
//Guerrier_des_Plaines::ArmePrincipale_GetName(), méthode d'accès publique
//renvoie la valeur du membre _armePrincipale;
string Guerrier_des_Plaines::ArmePrincipale_GetName() const
{
return _armePrincipale;
}
 
 
//Guerrier_des_Plaines::ArmeSecondaire_GetName(), méthode d'accès publique
//renvoie la valeur du membre _armeSecondaire;
string Guerrier_des_Plaines::ArmeSecondaire_GetName() const
{
return _armeSecondaire;
}
 
 
//définition de Guerrier_des_Plaines::ArmePrincipale_SetName(), méthode d'accès publique
//définit le membre _armePrincipale
void Guerrier_des_Plaines::ArmePrincipale_SetName(string armePrincipale)
{
//initialise la variable membre _armePrincipale
//avec la valeur passée par le paramètre armePrincipale
_armePrincipale = armePrincipale;
}
 
//définition de Guerrier_des_Plaines::ArmeSecondaire_SetName(), méthode d'accès publique
//définit le membre _armeSecondaire
void Guerrier_des_Plaines::ArmeSecondaire_SetName(string armeSecondaire)
{
//initialise la variable membre _armePrincipale
//avec la valeur passée par le paramètre armePrincipale
_armeSecondaire = armeSecondaire;
} 

 

   Remarques semblables à celles du fichier précédent. wink

      Fichier Pont.cpp 

 

//Projet 106Class_13
//Fichier : Pont.cpp
//Définition de la fonction Echange_arme().cpp
 
//Utilisation d'une fonction amie commune comme pont entre deux classes
 
#include<iostream>
#include "Guerrier_des_Plaines.h"
#include "Guerrier_du_Nord.h"
 
using namespace std;
 
//DEFINITION DE LA FONCTION Echange_arme(Guerrier_du_Nord, Guerrier_des_Plaines)
void Echange_arme(Guerrier_du_Nord Thorgul, Guerrier_des_Plaines Ytzak)
{
cout << "Cette fonction va intervertir les armes secondaires de chaque objet des classes." << endl;
 
//On donne les armes à chaque guerrier
Thorgul.ArmePrincipale_SetName("Lourde hache d'eclair de Thor");
 
Ytzak.ArmePrincipale_SetName("Arc perforant de la douleur");
Thorgul.ArmeSecondaire_SetName("Poignard devoreur de chair");
 
Ytzak.ArmeSecondaire_SetName("Lame de feu buveuse d'ames");
 
//On affiche l'arme principale et secondaire de chaque guerrier
cout << "Armes de chaque guerrier :" << endl;
cout << "\nArme principale de Guerrier_du_Nord : " << Thorgul.ArmePrincipale_GetName() << endl;
cout << "Arme principale de Guerrier_des_Plaines : " << Ytzak.ArmePrincipale_GetName() << endl;
cout << "Arme secondaire de Guerrier_du_Nord : " << Thorgul.ArmeSecondaire_GetName() << endl;
cout << "Arme secondaire de Guerrier_des_Plaines : " << Ytzak.ArmeSecondaire_GetName() << endl;
 
//Les deux guerriers échangent leur arme secondaire
cout << "\nMaintenant chaque guerrier decide d'offrir son arme secondaire a l'autre." << endl;
Thorgul.ArmeSecondaire_SetName("Lame de feu buveuse d'ames");
Ytzak.ArmeSecondaire_SetName("Poignard devoreur de chair");
 
//Lecture des armes de chaque querrier
cout << "Armes de chaque guerrier :" << endl;
cout << "\nArme principale de Guerrier_du_Nord : " << Thorgul.ArmePrincipale_GetName() << endl;
cout << "Arme principale de Guerrier_des_Plaines : " << Ytzak.ArmePrincipale_GetName() << endl;
cout << "Arme secondaire de Guerrier_du_Nord : " << Thorgul.ArmeSecondaire_GetName() << endl;
cout << "Arme secondaire de Guerrier_des_Plaines : " << Ytzak.ArmeSecondaire_GetName() << endl;
 
//L'amitié est à utiliser avec prudence !
cout << "\nPar contre, ce que l'on pourrait egalement faire c'est changer volontairement" << endl;
cout << "une arme d'un objet d'une classe par une arme d'un objet de l'autre classe" << endl;
cout << "Et ca c'est pas bien !";
cout << " L'AMITIE C'EST BEAU MAIS IL FAUT S'EN MEFIER !!!" << endl;
cout << "\nExemple : Yzak.ArmePrincipale_SetName(Thorgul.ArmePrincipale_GetName())" << endl;
Ytzak.ArmePrincipale_SetName(Thorgul.ArmePrincipale_GetName());
cout << "Ce qui donnerait :" << endl;
cout << "\nArme principale de Guerrier_du_Nord : " << Thorgul.ArmePrincipale_GetName() << endl;
cout << "Arme principale de Guerrier_des_Plaines : " << Ytzak.ArmePrincipale_GetName() << endl;
} 

 

   Tout l'intérêt de la fonction Echange_arme() se retrouve dans les deux lignes où les deux guerriers échangent leur arme secondaire par l'intermédiaire de la méthode d'accès public ArmeSecondaire_SetName(). Ceci n'a été rendu possible qu'en déclarant la fonction Echange_arme() comme fonction ''amie'' agissant comme pont entre les deux classes Guerrier_du_Nord et Guerrier_des_Plaines.

   Une fonction amie d'une classe permet donc d'accéder aux membres privés de cette classe et ne doit être utilisée que dans certains cas réellement nécessaires. Une implémentation massive de fonctions amies entre plusieurs classes dans un projet reviendrait à fragiliser l'aspect boîte noire de la POO au profit d'un retour à une utilisation peu judicieuse de variables globales. frown

 

      Fichier 106Class_13mainFile.cpp 

 

//projet 106Class_13
 
//Fichier : 106Class_13mainFile.cpp
//Définition de la fonction main()
//Utilisation d'une fonction amie commune comme pont entre deux classes
 
#include <iostream>
#include <string>
#include "Guerrier_des_Plaines.h"
#include "Guerrier_du_Nord.h"
 
#include <conio.h>
 
using namespace std;
 
 
//On crée un objet Guerrier_du_Nord et un objet Guerrier_des_Plaines.
//On définit leurs armes principales et secondaires et on appelle la fonction Echange_arme()
//pour affichher leurs membres privés et en intervertir un.
int main()
{
string chaine1, chaine2;
 
Guerrier_du_Nord Thorgul(chaine1, chaine2);
Guerrier_des_Plaines Ytzak(chaine1, chaine2);
 
//Echange des armes secondaires
Echange_arme(Thorgul, Ytzak);
 
cin.get();
return 0;
} 

 

    La fonction main() crée simplement une instance de chaque classe et appelle la fonction Echange_arme().

   Ce qui affiche dans la console :

 

 

    Allez, un petit challenge peut-être ? angel

 

   Exercice 2 (Un petit challenge)

   Un membre du site m'a demandé comment envoyer un nom faisant partie d'un vector de type chaîne d'une classe ''Personne'' vers une classe ''Menu''.

   Ceci peut se faire en utilisant une fonction amie agissant comme pont entre les classes ''Personne'' et ''Menu''. Ben oui, c'est comme si un ami vous disait comment envoyer votre belle-mère vers une une autre planète... laugh Allez, en route ! indecision

 


   4 – Association de classes et pointeurs

 

   Ok, modifions notre projet 106Class_13. Nous pourrions aller chercher nos armes à partir d'une classe ''Arme_tranchante'' et une classe ''Arme_de_traits'', afin que les armes principales de nos deux guerriers ne fassent plus partie de leur classe propre mais là, nous allons devoir réutiliser les pointeurs et l'allocation dynamique de mémoire. cool

   Nous voyons ceci dans un nouveau projet.

 

   Exemple : Projet 107Class_14

      Fichier Arme_tranchante.h 

 

//Projet 107Class_14
//Fichier Arme_tranchante.h
//Pointeur vers une classe
 
#ifndef DEF_ARME_TRANCHANTE
#define DEF_ARME_TRANCHANTE
 
#include <iostream>
#include <string>
 
 
class Arme_tranchante
{
public:
 
Arme_tranchante(std::string nom, int degats);
void afficher();
 
private:
 
std::string _nom;
int _degats;
};
#endif 

 

   Dans le fichier Arme_tranchante.h, nous déclarons simplement un constructeur prenant le nom de l'arme et ses dégats en paramètres. Une fonction afficher() est également déclarée à la suite.

      Fichier Arme_de_traits.h

 

//Projet 107Class_14
//Fichier Arme_de_traits.h
//Pointeur vers une classe
 
#ifndef DEF_ARME_DE_TRAITS
#define DEF_ARME_DE_TRAITS
 
#include <iostream>
#include <string>
 
class Arme_de_traits
{
public:
 
Arme_de_traits(std::string nom, int degats, int portee);
//void changer(std::string nom, int degats);
void afficher();
 
private:
 
std::string _nom;
int _degats;
int _portee;
};
#endif 

 

   Et nous faisons les mêmes déclarations dans le fichier Arme_de_traits.h.

      Fichier Guerrier_du_nord.h

 

//Projet 107Class_14
//Fichier Guerrier_du_Nord.h
//Pointeur vers une classe
 
#ifndef DEF_GUERRIER_DU_NORD
#define DEF_GUERRIER_DU_NORD
 
#include <iostream>
#include <string>
#include "Arme_tranchante.h"
 
 
class Guerrier_du_Nord
{
public:
 
Guerrier_du_Nord();
Guerrier_du_Nord(std::string nom_Arme, int degats_Arme);
 
~Guerrier_du_Nord();
 
void Status();
 
private:
 
//La classe Guerrier_du_Nord contient un pointeur vers
//un objet de la classe Arme_tranchante
Arme_tranchante *_arme_tranchante;
 
};
#endif 

 

   La classe Guerrier_du_Nord déclare un constructeur surchargé qui prend le nom de l'arme ainsi que ses dégats en paramètres. La fonction Status() fera quant à elle appel à une fonction d'affichage.

   Dans la partie privée de la classe, nous déclarons un pointeur *_arme_tranchante de type Arme_tranchante. Ceci veut dire que l'arme détenue par le Guerrier du Nord n'est pas lié à sa propre classe mais appartient bel et bien à la classe Arme_tranchante.

 

       Fichier Guerrier_des_Plaines.h

 

 

//Projet 107Class_14
 
//Fichier Guerrier_des_Plaines.h
//Pointeur vers une classe
 
#ifndef DEF_GUERRIER_DES_PLAINES
#define DEF_GUERRIER_DES_PLAINES
 
#include <iostream>
#include <string>
 
#include "Arme_de_traits.h"
 
 
class Guerrier_des_Plaines
{
public:
 
Guerrier_des_Plaines();
Guerrier_des_Plaines(std::string nom_Arme, int degats_Arme, int portee);
 
~Guerrier_des_Plaines();
 
void Status();
 
private:
 
//La classe Guerrier_des_Plaines contient un pointeur vers
//un objet de la classe Arme_de_traits
Arme_de_traits *_arme_de_traits;
 
};
#endif 

 

  L'arme détenue par le Guerrier des Plaines étant un arc, nous avons ajouté la variable ''portee'' dans la déclaration de son constructeur. Des commentaires complémentaires sont déjà présents dans le fichier précédent. wink

      Fichier Arme_tranchante.cpp

 

//Projet 107Class_14
//Fichier Arme_tranchante.cpp
//Pointeur vers une classe
 
#include "Arme_tranchante.h"
using namespace std;
 
Arme_tranchante::Arme_tranchante(string nom, int degats) : _nom(nom), _degats(degats) {}
 
void Arme_tranchante::afficher()
{
cout << "Arme : " << _nom << endl;
cout << "Degats : " << _degats << endl;
} 

 

  Voyons le constructeur de la classe Arme_tranchante. Dans celui-ci, nous utilisons une liste d'initialisation (voir Chapitre 16, § 1 – Utilisation d'une liste d'initialisation dans l'implémentation d'un constructeur ou d'une fonction membre).

   La fonction membre afficher(), comme son nom l'indique, affichera les nom et dégats de l'arme à l'écran. wink

 

      Fichier Arme_de_traits.cpp :

 

//Projet 107Class_14
//Fichier Arme_de_traits.cpp
//Pointeur vers une classe
 
#include "Arme_de_traits.h"
 
using namespace std;
 
Arme_de_traits::Arme_de_traits(string nom, int degats, int portee) : _nom(nom), _degats(degats), _portee(portee) {}
 
void Arme_de_traits::afficher()
{
cout << "Arme : " << _nom << endl;
cout << "Degats : " << _degats << endl;
cout << "Portee : " << _portee << endl;
} 

 

   Et nous nous reportons aux commentaires du fichier précédent pour les explications concernant le fichier Arme_de_traits.cpp.

      Fichier Guerrier_du_Nord.cpp :

 

//Projet 107Class_14
//Fichier Guerrier_du_Nord.cpp
//Pointeur vers une classe
 
#include "Guerrier_du_Nord.h"
 
using namespace std;
 
Guerrier_du_Nord::Guerrier_du_Nord(string nomArme, int degatsArme)
{
_arme_tranchante = new Arme_tranchante(nomArme, degatsArme);
}
 
 
Guerrier_du_Nord::~Guerrier_du_Nord()
{
delete _arme_tranchante;
}
 
 
void Guerrier_du_Nord::Status()
{
_arme_tranchante->afficher();
} 

 

   *_arme_tranchante étant un pointeur de la partie privée de la classe Guerrier_du_Nord pointant vers un objet de la classe Arme_tranchante, nous faisons la réservation sur le tas dans le constructeur de Guerrier_du_nord.

   Le destructeur quant à lui libère la mémoire réservée à cet effet par l'instruction delete. N'oubliez pas que dans ce cas, si notre Guerrier du Nord venait à mourir et que nous ne libérions pas la mémoire réservée à l'arme tranchante, le pointeur, toujours existant, pointerait dès lors vers un emplacement mémoire inexistant, ce qui créerait, nous en avons déjà parlé, un pointeur fou qui nous conduirait directement au crash de notre programme. surprise

   Ensuite, la fonction Status() appelle la fonction afficher() par l'intermédiaire de l'opérateur ''flèche'' (voir chapitre 14 – Projet 089Struct_7).

 

      Fichier Guerrier_des_Plaines.cpp 

 

//Projet 107Class_14
//Fichier Guerrier_des_Plaines.h
//Pointeur vers une classe
 
#include "Guerrier_des_Plaines.h"
 
using namespace std;
 
Guerrier_des_Plaines::Guerrier_des_Plaines(string nomArme, int degatsArme, int portee)
{
_arme_de_traits = new Arme_de_traits(nomArme, degatsArme, portee);
}
 
Guerrier_des_Plaines::~Guerrier_des_Plaines()
{
delete _arme_de_traits;
}
 
void Guerrier_des_Plaines::Status()
{
_arme_de_traits->afficher();
} 

 

   Les commentaires relatifs au fichier Guerrier_des_Plaines.cpp sont bien entendu les mêmes que ceux du fichier Guerrier_du_Nord.cppcheeky

     Fichier 107Class_14mainFile.cpp

 

//Projet 107Class_14
//Fichier 107Class_14mainFile.cpp
//Pointeur vers une classe
 
#include <iostream>
#include <string>
#include "Guerrier_du_Nord.h"
#include "Guerrier_des_Plaines.h"
 
using namespace std;
 
 
int main()
{
// Création des personnages
Guerrier_du_Nord Thorgul("Lourde hache d'eclair de Thor", 10);
Guerrier_des_Plaines Ytzak("Arc perforant de la douleur", 10, 55);
 
//On affiche le statut de chaque guerrier
cout << endl;
cout << "Thorgul" << endl;
Thorgul.Status();
cout << endl;
cout << "Ytzak" << endl;
Ytzak.Status();
 
cin.get();
 
return 0;
} 

 

   Dans la fonction main(), nous créons les objets ''Thorgul'' et ''Ytzak''. Le constructeur de Guerrier_du_Nord passe le nom et les dégats d'une arme tranchante en paramètres tandis que le constructeur de Guerrier_des_Plaines passe le nom d'une arme de traits, ses dégats et la portée de celle-ci. wink

   Dans cette fonction, nous appelons la fonction Status() se rapportant à chacun des deux objets et qui ne fait qu'afficher les données concernant notre arme tranchante et notre arme de jet.

   Ce qui donne dans la console :

 

 

Remarque : Ce genre d'implémentation est intéressant dans le cas où nous désirons supprimer les items appartenant à un personnage en même temps que celui-ci mais ne serait pas applicable dans le cas où un héros voudrait récupérer ces items afin de les ajouter dans son propre inventaire. Nous verrons à aborder ce sujet dans un prochain chapitre de notre tutoriel. wink

 

   5 – Corrigés des exercices du chapitre 16


      Exercice 1

   Dans cet exercice on demandait :

   Réécrivez le projet 095Class_2 du chapitre 15 en initialisant un constructeur sous la forme de liste d'initialisation ainsi que les fonctions membres dans le fichier 095Class_2.h.

   Voici le code :

 

   Fichier Chap16_Ex1.h

 

//projet Chap16Exercice_1
//Fichier : Chap16Ex_1.h
//Déclaration de la classe ennemi
#ifndef DEF_CHAP16EX_1
#define DEF_CHAP16EX_1
 
#include <iostream>
#include <string>
 
class ennemi
 
{
public:
//Constructeur
ennemi() : _nom("Orc") {}
 
//Fonctions membres d'accès publiques
//Assignation de donnée
void set_Name(std::string name) { _nom = name; }
//Acquisition de donnée
std::string get_Name() { return _nom; }
 
private:
//Donnée membre privée
std::string _nom;
 
};
#endif 

 

   Et bien, il n'y avait rien de bien difficile à réaliser dans ce fichier header. En vous référant au chapitre 16 (§ 1 – Utilisation d'une liste d'initialisation dans l'implémentation d'un constructeur ou d'une fonction membre), vous pouviez très facilement arriver à ce résultat. cheeky

      Fichier Chap16_Ex1.cpp

 

//projet Chap16Exercice_1
//Fichier : Chap16Ex_1.cpp
//Fonction main()
#include "Chap16Ex_1.h"
#include <conio.h>
 
using namespace std;
 
int main()
{
//Création d'un objet de type ennemi
ennemi monEnnemi;
 
//Acquisition de la variable privée _nom par l'intermédiaire de la méthode d'accès
//get_Name()
cout << endl;
 
cout << "Acquisition de la variable privee _nom par l'intermediaire de la " << endl;
cout << "methode d'acces get_Name()" << endl;
cout << endl;
cout << "Mon ennemi est un " << monEnnemi.get_Name() << endl;
 
_getch();
return 0;
} 

 

   Dans la fonction main(), il suffit simplement de créer un objet ennemi et d'appeler la fonction membre d'accès publique getName() pour avoir accès au nom de votre ennemi préféré. Rien de très difficile dans cet exercice donc. wink

   Et ceci nous donne comme résultat dans la console :

 

 

      Exercice 2

   Dans cet exercice on demandait :

   Réécrivez le fichier 097Class_4.h en utilisant une liste d'initialisation et en implémentant les définitions des méthodes membres dans ce même fichier.

   Voici le code :

 

      Fichier Chap16_Exercice_2.h

 

//projet Chap16Exercice_2
//Fichier : Chap16Ex_2.h
//Déclaration de la classe Bouclier
//Méthodes surchargées
#ifndef CHAP16EX_2
#define CHAP16EX_2
 
#include <iostream>
#include <string>
 
using uint = unsigned int;
 
class Bouclier
{
public:
//Constructeur par défaut
Bouclier(std::string nom, std::string classe, uint prix_Achat, uint defense, uint
resistance) :
 
_name(nom), _class(classe), _PA(prix_Achat), _defense(defense),
_resistance(resistance) {};
 
//Destructeur
~Bouclier() {};
 
//Méthode Lire_Arme()
void Lire_Arme() const { Lire_Arme(_name, _class, _PA, _defense, _resistance); }
 
//Méthodes surchargées()
void Lire_Arme(std::string nom, std::string classe, uint prix_Achat, uint defenseuint resistance) const
{
std::cout << "Nom : " << nom << std::endl;
std::cout << "Classe : " << classe << std::endl;
std::cout << "Prix achat : " << prix_Achat << std::endl;
std::cout << "Defense : " << defense << std::endl;
std::cout << "Resistance : " << resistance << std::endl;
}
 
void Lire_Arme(std::string nom) const { std::cout << "Nom : " << nom << std::endl; }
 
void Lire_Arme(std::string nom, std::string classe) const
{
std::cout << "Nom : " << nom << std::endl;
std::cout << "Classe : " << classe << std::endl;
}
 
void Lire_Arme(std::string nom, std::string classe, uint prix_Achat) const
{
std::cout << "Nom : " << nom << std::endl;
std::cout << "Classe : " << classe << std::endl;
std::cout << "Prix d'achat : " << prix_Achat << std::endl;
}
 
void Lire_Arme(std::string nom, uint prix_Achat) const
{
std::cout << "Nom : " << nom << std::endl;
std::cout << "Prix d'achat : " << prix_Achat << std::endl;
}
 
void Lire_Arme(std::string nom, uint defense, uint resistance) const
{
std::cout << "Nom : " << nom << std::endl;
std::cout << "Defense : " << defense << std::endl;
std::cout << "Resistance : " << resistance << std::endl;
}
 
 
private:
//Données membres privées
std::string _name;
std::string _class;
uint _PA;
uint _defense;
uint _resistance;
 
};
#endif 

 

   Voila, rien de difficile là-dedans. Si vous le désirez vous pouvez revoir les paragraphes 1.1 et 1.2 du chapitre 16.  wink

   Et voilà, c'est tout en ce qui concerne ce chapitre. cool

   Les corrigés des exercices de ce chapitre seront présentés à la fin du chapitre 18.

      @ bientôt pour le chapitre 18 – Surcharge des opérateurs (1).                                                                     

            Gondulzak.  angel
 
 
 

Connexion

CoalaWeb Traffic

Today54
Yesterday282
This week848
This month3141
Total1742348

19/04/24