Big Tuto : Apprenez le C++

Chapitre 15 : Introduction aux classes (1)

 

Tutoriel présenté par : Robert Gillard (Gondulzac)
Date de publication : 25 septembre 2016
Date de révision : 13 novembre 2016

 

     Préliminaires

 

   Dans le chapitre 14 (Types de données complexes - 2ème partie), nous avons vu que nous pouvions déjà écrire quelques lignes de codes intéressantes au niveau des données et fonctions membres dans les structures. wink

   Nous savons cependant que les données et les fonctions membres d'une structure sont des éléments d'accès ''public'' par défaut, ce qui veut dire que ces données peuvent être modifiées en dehors de la structure et que d'autres fonctions pourraient y avoir accès soit pour modifier certaines données ou même les supprimer. surprise

   Sans vouloir réécrire tout ce qui a déjà été dit sur les avantages qu'apporte une classe par rapport à une structure, je signalerai qu'une classe permet une bien plus grande protection de ses données car celles-ci peuvent être déclarées ''private'' (privées). Un autre degré de protection des données est de les déclarer ''protected'' (protégées) mais ce type de protection serait plutôt à réserver lors de la dérivation des classes. Nous parlerons de tout ceci en temps utile mais nous allons commencer sans nous prendre la tête, par la simple déclaration d'une classe. wink

 

Retrouvez les projets complets de ce chapitre :

  

 

 

   1 – Déclaration d'une classe

 

   Déclarer une classe se fait comme la déclaration d'une structure, à part que le mot-réservé ''class'' remplace le mot-clé ''struct''. smiley

   Pour une première approche, nous pouvons utiliser la déclaration de l'exemple du projet 089Struct_7 du chapitre 14 en gardant à l'esprit que nous allons déclarer une classe. Cette déclaration se ferait donc comme ceci : 

 

class arme_tranchante
{
//Données membres
string nom;
double prix_achat;
double prix_revente;
uint degats;
 
//Fonctions membres
void Ajouter_arme();
void Lire_arme();
};

 

   Vous pourriez me dire qu'à part le mot-clé ''class'' qui remplace le mot ''struct'', cette déclaration est exactement la même que celle du projet du chapitre 14 ? frown

   Et bien vous auriez raison et tort à la fois. cheeky Raison, parce que les données membres ainsi que les fonctions membres ont été recopiées telles quelles mais vous auriez tort parce que cette déclaration a une incidence tout-à-fait différente de la même déclaration déclarée ''struct'' et amène à un résultat inattendu lors de la compilation. Avant de m'expliquer plus avant sur mes propos, je vous invite à remplacer simplement le mot-clé ''struct'' par ''class'' dans la déclaration du projet 089Struct_7 et d'ensuite admirer les belles erreurs que votre compilateur préféré va vous envoyer au visage indecision :

- il ne peut pas accéder aux membres privés déclarés dans la classe arme_tranchante.
- les fonctions Ajouter_arme() et Lire_arme() sont inaccessibles.

   Pourquoi ? blush

   Je rappelle que dans une structure, les données et fonctions membres sont ''public'' par défaut, mais il en va tout autrement en ce qui concerne la déclaration d'une classe. En effet, dans une classe, les données et fonctions membres sont ''private'' par défaut. Alors, comment accéder aux membres d'une classe ? frown

 

   2 – Les modes d'accès ''private'' et ''public''

 

   En POO, Il est fortement recommandé que les données (variables membres) d'une classe restent privées. Dans ce cas, l'accès à ces données doit se faire par l'intermédiaire de ''fonctions membres publiques''. Pour ce faire, le type d'accès doit être affiché dans la déclaration d'une classe (notez bien que tous les membres d'une classe étant ''private'' par défaut, ce type d'accès ne doit pas obligatoirement être signalé mais que pour une raison de meilleure ''vision'' de ces différents types, ceux-ci seront toujours renseignés lors de la déclaration de nos classes). wink

   Pour garder une certaine compatibilité d'écriture avec les tutoriels du Big Tuto SFML 2.0 et Action A-rpg de notre ami Jay, nous conviendrons de déclarer les fonctions membres ''public'' en premier et de les faire suivre par les données privées. smiley

   Je dois faire remarquer que certains auteurs font précéder les données membres d'un ''m'' minuscule, pour rappeler qu'il s'agit bien de ''variables membres'' et que d'autres utilisent le caractère de soulignement pour la distinction de ces variables. Pour la suite de nos tutoriels C++, je préférerai cette dernière option. wink

   Nous pouvons maintenant revoir la déclaration de notre classe arme_tranchante que nous venons d'afficher un peu plus haut :

 

class arme_tranchante
{
public:
//Fonctions membres (publiques)
void Ajouter_arme();
void Lire_arme();
 
private:
//Données membres (privées)
string _nom;
double _prix_achat;
double _prix_revente;
uint _degats;
};

 

   Bien, cette déclaration vous indique que les données membres de la classe sont privées et que l'on ne pourra y avoir accès qu'à l'aide des fonctions membres publiques. Nous pouvons voir maintenant quelles en sont les incidences sur l'accès et la protection des données. wink

 

   Cette façon de déclarer une classe se nomme l'''encapsulation des données'' et donne à la classe un aspect ''boîte noire'' où les données sont protégées, inaccessibles à des fonctions ne faisant pas partie de la classe.

 

   Un membre privé ne sera atteint qu'à partir d'une fonction (méthode) de la même classe alors qu'un membre public le sera à partir d'un objet créé à partir de cette classe. Nous avons vu comment créer un objet à partir d'une structure dans le chapitre précédent. Si nous voulons créer un objet de la classe ''arme_tranchante'' , nous écririons par exemple dans la fonction main() :

arme_tranchante monArme;

   Déclaration dans laquelle ''arme_tranchante'' est le nom de la classe et ''monArme'' un objet créé à partir de cette classe.

   Ce que nous venons de dire un peu plus haut nous interdit cependant d'accéder directement à une variable privée à partir d'une instance de classe. En effet, si nous voulons modifier le nom de notre arme à partir de l'objet monArme, il nous est interdit d'écrire :

monArme._nom = ''Epee longue'';

   Ceci aurait pour effet de fâcher le compilateur et lui faire dire ce que nous avions écrit en début de chapitre :

''Je ne peux pas accéder aux membres privés déclarés dans la classe arme_tranchante''.

   Et en effet, nous ne pouvons y accéder que par l'intermédiaire d'une fonction membre publique. wink


   3 – Implémentation des méthodes d'une classe

 

   Implémenter une méthode de classe est facile, nous l'avons vu dans le chapitre précédent avec les fonctions membres d'une structure. Le procédé est exactement le même : l'implémentation des méthodes d'une classe se fait en écrivant en premier le type de retour de la méthode, suivi du nom de la classe, de l'opérateur de résolution de portée et du nom de la méthode. Nous allons le revoir dans le projet qui suit. smiley

   Nous allons réécrire en entier le projet 089Struct_7 en déclarant une classe à la place d'une structure afin de mieux nous rendre compte des méthodes d'accès et de leur implémentation. 

 

//projet 094Class_1
//Implémentation de données et de fonctions membres dans une classe
#include<iostream>
#include<string>
#include<conio.h>
 
using namespace std;
using uint = unsigned int;
 
class arme_tranchante
{
public:
//Fonctions membres (publiques)
void Ajouter_arme();
void Lire_arme();
 
private:
//Données membres (privées)
string _nom;
double _prix_achat;
double _prix_revente;
uint _degats;
 
};
 
//Implémentation de la méthode Ajouter_arme()
void arme_tranchante::Ajouter_arme()
{
cout << "Nom : ";
getline(cin, _nom);
cout << "Prix d'achat : ";
cin >> _prix_achat;
cout << "Prix de revente : ";
cin >> _prix_revente;
cout << "Degats : ";
cin >> _degats;
cin.ignore();
}
 
//Implémentation de la méthode Lire_arme()
void arme_tranchante::Lire_arme()
{
cout << "Nom : " << _nom << endl;
cout << "Prix d'achat : " << _prix_achat << endl;
cout << "Prix de revente : " << _prix_revente << endl;
cout << "Degats : " << _degats << endl;
}
 
int main()
{
//Création d'un objet de type arme_tranchante et un tableau d'objets
//Un tableau de 2 armes pour ne pas avoir trop de saisie à effectuer
const uint nb_armes(2);
arme_tranchante monArme;
arme_tranchante monTableauArmes[nb_armes];
 
//Appel des fonctions membres
cout << endl;
cout << "Donnes membres de l'objet 'monArme'" << endl << endl;
 
monArme.Ajouter_arme();
cout << endl;
 
cout << "Lecture des membres de l'objet 'monArme'" << endl << endl;
monArme.Lire_arme();
 
cout << endl;
cout << "Saisissez les champs suivants pour remplir les objets monTableauArmes'" << endl << endl;
 
for (uint i = 0; i != nb_armes; ++i)
monTableauArmes[i].Ajouter_arme();
 
cout << endl;
cout << "Lecture des elements du tableau 'monTableauArmes'" << endl << endl;
 
for (uint i = 0; i != nb_armes; ++i)
monTableauArmes[i].Lire_arme();
 
_getch();
return 0;
}

 

   Si vous comparez ce projet où une classe est mise en œuvre et le projet 089Struct_7 du chapitre 14, vous ne verrez pas de réelle différence mais pourtant il y en a une, et de taille ! indecision

   Si vous avez bien lu et compris tout ce qui a été dit jusqu'ici dans ce chapitre vous conviendrez que si, dans le projet 089Struct_7 où nous utilisons une structure, nous ajoutons les lignes suivantes dans la fonction main() :

monTableauArmes[1].prix_achat = 600;
cout << monTableauArmes[1].prix_achat << endl;
 

   Ce projet compilera parfaitement et donnera bien 600 comme résultat. Par contre, si vous ajoutez ces lignes dans la fonction main() du programme ci-dessus où nous utilisons une classe, le compilateur va de nouveau vous insulter en vous disant qu'il ne peut pas avoir accès aux éléments privés d'une classe (Hé, quand-même, n'importe qui ne peut pas augmenter les prix de cette façon... laugh)

   Mais qu'importe, nous verrons bientôt comment accéder aux membres privés d'une classe à l'aide de fonctions membres appelées méthodes membres d'accès publique ou ''accesseurs''.

   Et je ne donne pas le résultat de ce projet dans la console puisqu'il est identique à celui du projet 089Struct_7 du chapitre 14.

 

   4 – Découper un projet en plusieurs fichiers

 

   Il y a quelque temps j'avais annoncé que nous pourrions découper nos projets en plusieurs fichiers dès l'étude des classes et c'est ce que nous allons faire à partir de maintenant. wink

   Il ne s'agit pas de vous ennuyer à devoir manipuler deux ou trois fichiers par projet mais plutôt d'acquérir une bonne habitude en programmation C++ (vos codes seront plus lisibles lors de programmes plus conséquents angel) et de rester compatibles avec les tutoriels SFML et Action A-rpg, eux-mêmes utilisant cette architecture. wink

   Nous allons commencer avec le projet 094Class_1 que nous venons de voir ci-dessus. Je vous propose de découper ce projet en trois parties :

- un fichier .cpp que nous nommerons program_094 et qui contiendra la fonction main().
- un fichier .cpp que nous nommerons 094Class_2 contenant les définitions des méthodes.
- un fichier .h que nous nommerons 094Class_2 contenant les déclarations des méthodes.

 

//projet Program_094
//Fichier : 094Class_2.h
//Déclaration de la classe "arme_tranchante
#ifndef DEF_094CLASS_2
#define DEF_094CLASS_2
 
#include <iostream>
#include<string>
 
using uint = unsigned int;
 
class arme_tranchante
{
public:
//Fonctions membres (publiques)
void Ajouter_arme();
void Lire_arme();
 
private:
//Données membres (privées)
std::string _nom;
double _prix_achat;
double _prix_revente;
uint _degats;
};
#endif

 

   Dans ce fichier header, nous n'oublions pas d'inclure le fichier <iostream> ni le fichier <string>.

   Dans le cas de ce dernier, et vu que l'on ne peut pas mettre d'instruction using namespace std dans un fichier .h, nous sommes obligés d'ajouter l'espace de nom ''std'' devant le type de la variable membre privée _nom.

   Voyons maintenant le fichier 094Class_2.cpp qui contiendra les définitions des méthodes :

 

//projet Program_094
//Fichier : 094Class_2.cpp
//Implémentation des méthodes Ajouter_arme() et Lire_arme()
 
 
#include "094Class_2.h"
 
using namespace std;
 
//Implémentation de la méthode Ajouter_arme()
void arme_tranchante::Ajouter_arme()
{
cout << "Nom : ";
getline(cin, _nom);
cout << "Prix d'achat : ";
cin >> _prix_achat;
cout << "Prix de revente : ";
cin >> _prix_revente;
cout << "Degats : ";
cin >> _degats;
cin.ignore();
}
 
//Implémentation de la méthode Lire_arme()
void arme_tranchante::Lire_arme()
{
cout << "Nom : " << _nom << endl;
cout << "Prix d'achat : " << _prix_achat << endl;
cout << "Prix de revente : " << _prix_revente << endl;
cout << "Degats : " << _degats << endl;
}

 

   Et dans ce fichier nous incluons le fichier header "094Class_2.h" ainsi que la référence à l'espace de nom ''std'' : using namespace std;

   Et pour terminer nous affichons notre fichier program_094.cpp contenant notre fonction main().

 

//projet Program_094
//Fichier : Program_094.cpp
//Fonction main()
 
#include <conio.h>
#include "094Class_2.h"
 
using namespace std;
 
int main()
{
//Création d'un objet de type arme_tranchante et un tableau d'objets
//Un tableau de 2 armes pour ne pas avoir trop de saisie à effectuer
const uint nb_armes(2);
arme_tranchante monArme;
arme_tranchante monTableauArmes[nb_armes];
 
//Appel des fonctions membres
cout << endl;
cout << "Donnes membres de l'objet 'monArme'" << endl << endl;
monArme.Ajouter_arme();
cout << endl;
 
cout << "Lecture des membres de l'objet 'monArme'" << endl << endl;
monArme.Lire_arme();
 
cout << endl;
 
cout << "Saisissez les champs suivants pour remplir les objets monTableauArmes'" << endl << endl;
 
for (uint i = 0; i != nb_armes; ++i)
monTableauArmes[i].Ajouter_arme();
 
cout << endl;
cout << "Lecture des elements du tableau 'monTableauArmes'" << endl << endl;
 
for (uint i = 0; i != nb_armes; ++i)
monTableauArmes[i].Lire_arme();
 
_getch();
return 0;
}

 

   Fichier dans lequel nous n'oublions pas d'inclure le fichier <conio.h> ni le fichier header ''094Class_2.h''. Remarquez en outre qu'utilisant le flux de sortie ''ostream'', nous avons également besoin de faire référence à l'espace de nom ''std''.

   Voilà, je vous laisse bidouiller tout ça dans votre IDE préféré avant de découvrir comment accéder aux membres privés d'une classe. wink


   5 – Accéder aux membres privés d'une classe

 

   ''Mais tu viens de nous dire que le compilateur allait nous insulter si nous voulions tenter d'accéder aux membres privés d'une classe !?'' frown

   Oui c'est vrai, en tentant d'y accéder directement à partir d'une variable ''private'' mais nous avons un autre moyen. wink Rappelez-vous, je vous ai parlé de fonctions membres d'accès publique appelées ''accesseurs'', c'est ce que nous allons utiliser ici.

   Nous allons créer une classe ''ennemi'' ne comportant que deux fonctions membres d'accès publique. L'une d'entre-elles permettra de donner un nom (variable d'accès privé) à notre ennemi et l'autre de pouvoir afficher ce nom à l'écran. Il s'agit ici d'un exemple tout simple mais il vous montrera l'utilité des accesseurs. cheeky

   Certains programmeurs utilisent les termes ''accesseur'' et ''mutateur'' pour désigner des fonctions membres permettant la lecture et l'assignation de données. Je préférerai le terme plus général de ''Méthodes d'accès publique'' lesquelles seront préfixées du terme ''get_'' pour une méthode d'accès et ''set_'' pour une méthode d'assignation. Nous voyons cela de suite dans un nouveau projet. wink


   Projet 095Class_2 (fonctions membres d'accès publique)

        1. Fichier 095Class_2.h  

 

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

 

  Dans la partie ''public'' de la déclaration de notre classe, nous déclarons la méthode d'accès publique set_Name() qui prend une chaîne en paramètre ainsi que la méthode get_Name() qui va récupérer le nom de notre ennemi.

   Dans la partie ''private de la classe'', on déclare une variable membre que l'on fait précéder (nous en avons parlé plus haut wink) du caractère de soulignement.

 


        2. Fichier 095Class_2.cpp  

 

//projet 095Class_2
//Fichier : 095Class_2.cpp
//Implémentation des méthodes d'accès publique set_Name() et get_Name()
 
#include "095Class_2.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 ennemi::set_Name(string name)
{
_nom = name;
}
 
//Méthode d'accès publique d'acquisition (accesseur)
//Renvoie le contebu de la variable _nom
string ennemi::get_Name()
{
return _nom;
}

 

   Ce fichier implémente les deux fonctions d'accès publique et ne présente aucune difficulté, les commentaires étant contenus dans le fichier.

 


        3. Fichier 095Class_2mainFile.cpp  

 

//projet 095Class_2
//Fichier : 095Class_2mainFile.cpp
//Fonction main()
#include "095Class_2.h"
#include <conio.h>
 
using namespace std;
 
int main()
{
//Création d'un objet de type ennemi
 
ennemi monEnnemi;
 
//Assigation d'un nom à l'objet
monEnnemi.set_Name("Orc");
 
//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(), nous créons une instance de la classe ennemi, à qui l'on donne le nom ''monEnnemi''.

   Nous utilisons ensuite la fonction membre publique set_Name() afin d'assigner la chaîne ''orc'' à la variable ''name'' passée en paramètre à la fonction. A l'intérieur de la fonction, l'assignation de la chaîne à la variable privée _nom se fait par l'instruction _nom = name;. Nous avons bien accédé à une variable privée par l'intermédiaire d'une méthode publique. cool

   Et la fonction membre get_Name() retourne la chaîne contenue dans la variable privée _nom.

   Tout le monde est content et le compilateur aussi... angel

   Ce qui donne dans la console :

 

 

   Tiens oui, pourquoi pas un petit exercice ? Allez, un facile. wink

 

 

   Exercice 1

   Créez une classe ''arme_de_Traits''. Dans cette classe, implémentez des méthodes d'accès publique pour le nom et la portée de l'arme.

   Dans la fonction main() vous créerez un objet ''monArc'' qui affichera les nom et portée de l'arme à l'aide des méthodes d'accès publique correspondantes.

   Le nom et la portée de l'arme seront saisis au clavier dans la fonction main().

   Et vous devriez obtenir quelque chose de ce genre dans la console :

 

  

 

 

   6 – Fonctions membres ''const''

 

   Une bonne habitude de programmation (je parlerai plutôt de nécessité cheeky), est de déclarer et définir ''const'' toute fonction membre dont les variables privées ne peuvent pas être modifiées.
   Si nous nous référons au projet 095Class_2, la méthode d'accès publique get_Name() devrait être déclarée : 

std::string get_Name() const;

 

   et définie :

string ennemi::get_Name() const
{
return _nom;
}

 

   Ce que nous ferons dans tous nos projets par la suite si nous ne voulons pas avoir de surprises un jour. wink

   Quel genre de surprise ? surprise

   Supposons notre fonction d'accès déclarée sans le qualificateur const. Rien ne nous empêche alors d'écrire par exemple la méthode get_Name() comme ceci :

string ennemi::get_Name()
{
_nom = "Elfe";
return _nom;
}

 

   et dans ce cas, bien que vous ayez défini un Orc comme votre ennemi dans la fonction main(), dès l'appel de la méthode get_Name(), c'est bien un Elfe qui sera affiché... angry Ce n'est certainement pas ce que vous auriez désiré et pour éviter ceci, vous n'oublierez pas d'ajouter le spécificateur ''const'' là où c'est nécessaire. Dans ce cas, en cas d'erreur, c'est le compilateur qui vous rappellera à l'ordre. wink

 


   7 – Constructeurs et destructeurs

 

   Dans le chapitre 14, Types de données complexes (partie 2 - § 1.3), nous avons introduit la notion de constructeur et de destructeur. De même qu'avec les structures, les constructeurs sont des formes spéciales de fonctions membres qui portent le nom de la classe à laquelle ils se réfèrent et qui ne renvoient aucune valeur, même pas le type ''void''. Le destructeur, destiné à libérer la mémoire est, quant-à-lui, précédé du signe ''~'' (tilde).

   Nous allons maintenant approfondir ces notions au niveau des classes, car dans celles-ci les constructeurs et les destructeurs sont susceptibles de prendre des formes différentes. wink

 

         7.1 – Constructeur par défaut

 

   Vous devez premièrement savoir que si vous ne créez aucun constructeur ni destructeur dans une classe, c'est le compilateur qui se chargera d'en créer pour vous. wink Cette manière de faire est cependant beaucoup trop restrictive et il est bien plus intéressant et surtout plus utile de créer ses propres constructeurs. cheeky

   Un constructeur qui ne prend aucun paramètre est appelé un ''constructeur par défaut''. Ceux d'entre-vous qui suivent le big tuto SFML ou le big tuto Action A-rpg connaissent déjà ce type de constructeur qui ne prend aucun paramètre. smiley

   Par exemple, et pour n'en prendre que deux, les classes ''Map'' et ''Player'' de ces tutoriels feront appel à leur constructeur Map() et Player(). Il se fait qu'un seul constructeur par défaut est suffisant dans ces cas-ci mais il faut savoir que dans la plupart des jeux plus élaborés, certaines classes utilisent de nombreux constructeurs, et bien souvent plus d'une dizaine par classe. Pourquoi ? surprise

 

         7.2 – Constructeur surchargé

 

   Prenons par exemple une classe Player. Elle pourrait avoir un constructeur ayant le nom et la race du héros, soit Player(string, string). Un autre constructeur pourrait avoir le nom et l'age du héros, soit Player(string, int). Un autre pourrait recevoir les trois paramètres ou encore un autre avec un seul paramètre. Il n'y a de limite à la création de constructeurs que l'imagination du programmeur pour autant que les constructeurs qu'il définira aient bien entendu un sens et servent à quelque chose dans le programme. cheeky

   Ce genre de constructeur s'appelle ''constructeur surchargé''. On parlera alors de ''surcharge de constructeurs''.

 

   Exemple de constructeur surchargé :

   Nous prendrons l'exemple d'une classe Player quelconque possédant un constructeur par défaut et trois constructeurs surchargés.

 

   Projet 096Class_3 ( Constructeurs surchargés )

        1. Fichier 096Class_3.h  

 

//projet 096Class_3
//Fichier : 096Class_3.h
//Déclaration de la classe Player
//Constructeurs surchargés
#ifndef DEF_096CLASS_3
#define DEF_096CLASS_3
 
#include <iostream>
#include <string>
 
class Player
{
public:
//Constructeur par défaut
Player();
//Constructeurs surchargés
Player(std::string name);
Player(std::string name, std::string race);
Player(std::string name, std::string race, std::string sexe);
 
//Destructeur
~Player();
 
//Fonctions membres d'accès publique
//Acquisition de données
std::string get_Name() const;
std::string get_Race() const;
std::string get_Sexe() const;
 
private:
//Données membres privées
std::string _nom;
std::string _race;
std::string _sexe;
 
};
#endif

 

   Voici la définition de notre classe Player. Celle-ci possède quatre constructeur :

   - un constructeur par défaut,
   - un constructeur surchargé avec le nom passé en paramètre,
   - un constructeur surchargé avec le nom et la race passés en paramètres,
   - un constructeur surchargé avec le nom, la race et le sexe passés en paramètres.

   Nous avons en outre ajouté des fonctions membres d'accès publique afin d'accéder aux variables privées _nom, _race et _sexe.

 

        2. Fichier 096Class_3.cpp

   Nous passons maintenant au fichier de définitions :

 

//projet 096Class_3
//Fichier : 096Class_3.cpp
//Implémentation des différents constructeurs
//Implémentation des méthodes d'accès publique
 
#include "096Class_3.h"
#include <string>
 
using namespace std;
 
//Implémentation du constructeur par défaut
Player::Player()
{
_nom = "Aria";
_race = "humain";
_sexe = "feminin";
}
 
//Implémentation du 1er constructeur surchargé
Player::Player(string name)
{
_nom = name;
}
 
//Implémentation du 2eme constructeur surchargé
Player::Player(string name, string race)
{
_nom = name;
_race = race;
}
 
//Implémentation du 3eme constructeur surchargé
Player::Player(string name, string race, string sexe)
{
_nom = name;
_race = race;
_sexe = sexe;
}
 
//Implémentation du destructeur
Player::~Player() {}
 
//Implémentation des fonctions membres d'accès publique
string Player::get_Name() const { return _nom; }
 
string Player::get_Race() const { return _race; }
 
string Player::get_Sexe() const { return _sexe; }
 

 

   Et je ne vois rien de spécial à expliquer ici, l'implémentation des constructeurs surchargés se faisant facilement en donnant aux variables privées les valeurs des variables passées en paramètres. wink

 

        3. Fichier 096Class_3mainFile.cpp 

 

 //projet 096Class_3
//Fichier : 096Class_3mainFile.cpp
//Implémentation des différents constructeurs
//Implémentation des méthodes d'accès publique
 
#include "096Class_3.h"
#include <string>
#include <conio.h>
 
using namespace std;
 
int main()
{
//Création d'un objet de type Player
//Utilisation du constructeur par défaut
Player heros;
 
//On affiche les membres de l'objet
 
cout << endl;
cout << "On utilise le constructeur par defaut :" << endl << endl;
cout << "Nom : " << heros.get_Name() << endl;
cout << "Race : " << heros.get_Race() << endl;
cout << "Sexe : " << heros.get_Sexe() << endl << endl;
 
//Création d'un objet de type Player
//Utilisation du 1er constructeur surchargé (Player (string name))
Player second_heros("Godefroid");
 
//On affiche le nom de l'objet
cout << "On utilise le 1er constructeur surcharge (Player (string)) : " << endl << endl;
cout << "Nom : " << second_heros.get_Name() << endl << endl;
 
//Création d'un objet de type Player
//Utilisation du 2eme constructeur surchargé (Player (string, string))
Player troisieme_heros("Elwyn", "Elfe");
 
//On affiche le nom et la race de l'objet
cout << endl;
cout << "On utilise le 2eme constructeur surcharge (Player (string, string)) : " << endl << endl;
cout << "Nom : " << troisieme_heros.get_Name() << endl;
cout << "Race : " << troisieme_heros.get_Race() << endl << endl;
 
//Création d'un objet de type Player
//Utilisation du 3eme constructeur surchargé (Player (string, string, string))
Player quatrieme_heros("Aron", "Torgon", "Masculin");
 
//On affiche le nom, la race et le sexe de l'objet
cout << "On utilise le 3eme constructeur surcharge (Player (string, string, string)) : " << endl << endl;
cout << "Nom : " << quatrieme_heros.get_Name() << endl;
cout << "Race : " << quatrieme_heros.get_Race() << endl;
cout << "Sexe : " << quatrieme_heros.get_Sexe() << endl;
 
_getch();
return 0;
}

 

   Dans la fonction main(), nous créons quatre objets de type Player créés avec leur constructeur respectif.

   Les membres du constructeur par défaut ont été initialisés dans le fichier 096Class_3.cpp avec les chaînes ''Aria'', ''Humain'' et ''Feminin''.

   Les différents constructeurs surchargés ont, quant-à-eux initialisé les trois objets second_herostroisieme_heros et quatrieme_heros avec leurs membres respectifs.

   Vous voyez que les constructeurs surchargés ne sont pas difficiles à implémenter. Ils peuvent par contre permettre pas mal de possibilités dans l'élaboration de certaines classes. wink

   Et tout ceci nous affiche dans la console :

 

 

 

    Un petit challenge (Exercice 2)

 

   Dans cet exercice, pas de constructeur surchargé.

   Soit une classe Personnage définie comme suit :

 

class Personnage
{
public:
//Constructeur à qui on passe une référence sur un vector string
Personnage(vector<std::string>&noms);
 
//Destructeur
~Personnage();
 
//méthodes d'accès publiques
std::string get_Name() const;
void set_Name(std::string nom);
 
//fonction membre
void lire_noms(vector<std::string> &) const;
 
private:
std::string _name;
 
};
#endif 

 

   A partir de cette classe créez :

                   - un fichier .h de déclaration,
                   - un fichier .cpp de définition,
                   - un fichier .cpp qui contiendra la fonction main().

   Le constructeur est un constructeur par défaut que vous initialiserez avec les deux premiers noms de l'exemple précédent, soit ''Aria'' et ''Godefroid''.

   Les deux noms suivants, soit ''Elwyn'' et ''Aron'', bien que pouvant être ajoutés directement dans le vector, vous n'en ferez rien. Ces deux noms seront assignés à la variable nom de la méthode d'accès publique set_Name() et ajoutés dans le vector à l'aide de la méthode d'accès publique get_Name(). Cela vous permettra de bien comprendre le mécanisme des méthodes d'accès. wink

   Dans l'implémentation de la fonction membre lire_noms(), vous utiliserez la boucle ''for'' du C++ 11 avec une variable d'itération de spécificateur de type ''auto''.

   Pour terminer, affichez les quatre éléments du vector noms.
 


   8 – Corrigés des exercices du chapitre 14

 

         Corrigé de l'exercice 1 (un petit challenge) :

 

   L'énoncé de cet exercice étant un peu long à reproduire, je vous demanderai de bien vouloir vous reporter au chapitre 14 pour sa relecture. Merci de votre compréhension. wink

   Voici le code : 

 

//projet Chap14Exercice_1
//Fonctions membres dans une structure
#include<fstream>
#include<iostream>
#include<string>
#include<conio.h>
 
using namespace std;
using uint = unsigned int;
 
struct arme_tranchante
{
//Données membres
string nom;
double prix_achat;
double prix_revente;
uint degats;
 
//Fonctions membres
void Fichier_Armes_ajouter_arme();
void Fichier_Armes_lire_arme();
};
 
string nom_Fichier = "Armes.txt";
ofstream writeFile(nom_Fichier, ios::app);
 
void arme_tranchante::Fichier_Armes_ajouter_arme()
{
 
//Si le fichier a bien été crée...
if (writeFile)
{
cout << "Nom : ";
getline(cin, nom);
writeFile << nom << endl;
cout << "Prix d'achat : ";
cin >> prix_achat;
writeFile << prix_achat << endl;
cout << "Prix de revente : ";
cin >> prix_revente;
writeFile << prix_revente << endl;
cout << "Degats : ";
cin >> degats;
writeFile << degats << endl;
cin.ignore();
}
 
else
{
cout << "Erreur : le fichier n'a pu etre ouvert !" << endl;
}
 
}
 
 
void arme_tranchante::Fichier_Armes_lire_arme()
{
string nom_Fichier = "Armes.txt";
string ligne;
 
cout << endl;
cout << "Lecture du fichier " << nom_Fichier << endl << endl;
ifstream readFile(nom_Fichier);
 
if (readFile)
{
//On lit le fichier ligne par ligne
while (getline(readFile, ligne))
cout << ligne << endl;
}
 
else
cout << "Erreur : le fichier n'a pu etre ouvert !" << endl;
 
cout << endl;
cout << "***End of File***" << endl;
 
//Fermeture du fichier
readFile.close();
}
 
 
int main()
{
//Création d'un tableau monTableauArmes dans le tas avec renvoi d'un pointeur sur le
//tableau
const uint longueur_tableau(5);
arme_tranchante *monTableauArmes = new arme_tranchante[longueur_tableau];
arme_tranchante monFichier;
 
cout << endl;
cout << "On ajoute deux armes au fichier 'Armes.txt'" << endl << endl;
 
//Ecriture dans le fichier
for (uint i = 0; i != longueur_tableau - 3; ++i)
monTableauArmes[i].Fichier_Armes_ajouter_arme();
 
cout << endl;
cout << "***Le fichier a ete modifié***" << endl;
writeFile.close();
 
cout << endl << endl;
//Lecture du tableau
 
monFichier.Fichier_Armes_lire_arme();
 
delete[5] monTableauArmes;
 
_getch();
return 0;
} 

 

   Pour cet exercice vous aviez à votre disposition un fichier dénommé ''Armes.txt''. Ce fichier contient les enregistrements de trois armes différentes auxquels il faut en ajouter deux.

   En début de programme, une structure ''arme_tranchante'' est déclarée. Cette structure contient quatre variables et deux fonctions membres dénommées ''Fichier_Armes_ajouter_arme()'' et ''Fichier_Armes_lire_arme().

   Nous implémentons ces deux fonctions membres afin de pouvoir ajouter des enregistrements et lire les enregistrements du fichier.

   Avant utilisation du fichier par ''Fichier_Armes_ajouter_arme()'', nous devons ouvrir celui-ci en mode ajout d'où la ligne :

ofstream writeFile(nom_Fichier, ios::app);

 

   Dans la fonction main(), on réserve un tableau ''monTableauArmes'' de type arme_tranchante dans la tas avec new renvoyant un pointeur sur le début du tableau ainsi qu'un objet ''monFichier''.
 
   On ajoute ensuite deux enregistrements dans le fichier ''Armes.txt'', on lit le fichier complet et on n'oublie pas de libérer la mémoire que nous avons réservée pour le tableau.
 
   Vous deviez obtenir quelque chose de ce genre dans la console :
 

 

  

 

   Et vous pourriez même refaire facilement cet exercice en utilisant une classe à la place d'une structure, maintenant. wink

 

 

        Corrigé de l'exercice 2

 

 

   Pour cet exercice, on demandait de réécrire tout le projet 090Struct_8 en ajoutant un constructeur et un destructeur dans la structure.

   Voici le code :

 

 

//projet Chap14Exercice_2
//Utilisation d'un constructeur et d'un destructeur dans une structure
#include<iostream>
 
#include<string>
#include<conio.h>
 
using namespace std;
using uint = unsigned int;
 
struct arme_tranchante
{
//Données membres déclarées comme pointeurs
string *nom;
double *prix_achat;
double *prix_revente;
uint *degats;
 
//Constructeur
arme_tranchante();
 
//Destructeur
~arme_tranchante();
 
//Fonctions membres
void Ajouter_arme();
void Lire_arme();
};
 
 
//Implémentation du constructeur
arme_tranchante::arme_tranchante()
{
//Résevation mémoire
nom = new string;
prix_achat = new double;
prix_revente = new double;
degats = new uint;
}
 
 
//Implémentation du destructeur
arme_tranchante::~arme_tranchante()
{
//Liberation de la mémoire
delete nom;
delete prix_achat;
delete prix_revente;
delete degats;
}
 
//Implémentation des fonctions membres
void arme_tranchante::Ajouter_arme()
{
//Saisie
cout << "Nom : ";
getline(cin, *nom);
cout << "Prix d'achat : ";
cin >> *prix_achat;
cout << "Prix de revente : ";
cin >> *prix_revente;
cout << "Degats : ";
cin >> *degats;
cin.ignore();
}
 
 
void arme_tranchante::Lire_arme()
{
cout << "Nom : " << *nom << endl;
cout << "Prix d'achat : " << *prix_achat << endl;
cout << "Prix de revente : " << *prix_revente << endl;
cout << "Degats : " << *degats << endl;
}
 
 
int main()
{
//Création d'un objet de type arme_tranchante et un tableau d'objets
//Un tableau de 2 armes pour ne pas avoir trop de saisie à effectuer
const uint nb_armes(2);
arme_tranchante monArme;
arme_tranchante monTableauArmes[nb_armes];
 
//Appel du constructeur
arme_tranchante();
//Appel des fonctions membres
 
cout << endl;
cout << "Donnes membres de l'objet 'monArme'" << endl << endl;;
monArme.Ajouter_arme();
cout << endl;
cout << "Lecture des membres de l'objet 'monArme'" << endl << endl;
monArme.Lire_arme();
 
cout << endl;
cout << "Saisissez les champs suivants pour remplir les objets monTableauArmes'" << endl << endl;
 
for (uint i = 0; i != nb_armes; ++i)
monTableauArmes[i].Ajouter_arme();
 
cout << endl;
cout << "Lecture des elements du tableau 'monTableauArmes'" << endl << endl;
 
for (uint i = 0; i != nb_armes; ++i)
monTableauArmes[i].Lire_arme();
 
_getch();
return 0;
} 

 

   A l'intérieur de notre structure arme_tranchante, nous ajoutons un constructeur et un destructeur. Les données membres sont également déclarées comme pointeurs mais la différence est que cette fois, c'est le constructeur qui réserve la mémoire pour ces variables dans le tas. wink

   L'implémentation du destructeur se fait en appliquant un ''delete'' sur chaque variable, ce que nous n'avons pu faire dans le projet 090Struct_8 du chapitre 14. L'implémentation de nos fonctions membres se fait de la même manière que dans le projet 090Struct_8 mais cette fois, lorsque nous fermerons le programme, il sera fait automatiquement appel au destructeur qui va libérer la mémoire des variables utilisées. smiley

   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 16.

      @ bientôt pour le chapitre 16 – Introduction aux Classes (2).                                                                     

            Gondulzak.  angel
 

 

Connexion

CoalaWeb Traffic

Today78
Yesterday232
This week1104
This month3397
Total1742604

20/04/24