Big Tuto : Apprenez le C++

Chapitre 19 : Surcharge des opérateurs (2)

 

Tutoriel présenté par : Robert Gillard (Gondulzac)
Date de publication : 15 février 2017
Date de révision : -

 

Retrouvez les projets complets de ce chapitre :

  

 

 

   Préliminaires

 

    Dans le chapitre précédent nous avons vu comment surcharger les opérateurs unaires ++ et -- sous leur forme de pré/post incrémentation et pré/post décrémentation. Nous avons également vu comment implémenter des fonctions operator++() et operator--() sous la forme de fonctions membres ou de fonctions amies. smiley

   Dans ce chapitre et le suivant nous continuerons cette étude avec la surcharge d'opérateurs binaires +=, - =, +, -, =, la surcharge d'opérateurs d'entrées/sorties >> et << ainsi que le surcharge d'opérateurs de comparaison. Nous verrons bientôt que l'implémentation de fonctions de surcharge d'opérateurs sera nécessaire pour la réalisation d'opérations arithmétiques sur des objets. wink



   1 – Surcharge des opérateurs binaires ''+'', ''-'', ''='', ''+='' et ''- =''

      1.1 – Implémentation d'une fonction d'addition

 

   De même que dans le chapitre 18 nous avons fait précéder la notion de surcharge d'opérateur ++ par un exemple implémentant une fonction d'incrémentation, nous allons faire précéder la notion de surcharge d'opérateur d'addition ''+'' par un exemple initialisant une fonction d'addition.

   Pour cet exemple, nous allons créer un projet contenant une classe ''Nombre'' mais nous n'y implémenterons qu'un fichier header et un fichier mainfile.

 

   Exemple : Projet 114Surcharge_7

      Fichier Surcharge_7.h  

 

//projet 114Surcharge_7
//Fichier : Surcharge_7.h
//Déclaration de la classe Nombre
//Implémentation d'une fonction d'addition
 
#ifndef DEF_NOMBRE
#define DEF_NOMBRE
 
typedef unsigned uint;
 
class Nombre
{
public:
 
//Constructeurs
Nombre() : _valeur(0) {}
Nombre(uint valeur) : _valeur(valeur) {}
 
~Nombre() {}
 
// Fonctions membres d'accès public
uint get_Val() const { return _valeur; };
void Set_Val(uint val) { _valeur = val; }
 
//Fonction d'addition
const Nombre Ajoute(const Nombre &rhs) { return Nombre(_valeur + rhs.get_Val()); }
 
private:
 
//Donnée membre privée
uint _valeur;
 
};
#endif 

 

   Dans le fichier Surcharge_7.h nous créons premièrement un constructeur par défaut qui initialise la variable privée _valeur à zéro.

   Nous créons ensuite un second constructeur surchargé destiné quant-à-lui à créer deux objets à additionner.

   La fonction Ajoute() est quelque peu spéciale : celle-ci prend en paramètre une référence constante à un objet Nombre. Cet objet correspond au nombre à additionner à l'objet en cours. La fonction renvoie en outre un objet Nombre dont nous allons voir l'affectation dans le fichier Surcharge_7MainFile.cpp. wink


   Fichier Surcharge_7.MainFile.cpp  

 

//projet 114Surcharge_7
//Fichier : Surcharge_7MainFile.cpp
//Implémentation d'une fonction d'addition
//Fonction main()
 
#include <iostream>
 
#include "Surcharge_7.h"
#include <conio.h>
 
using namespace std;
 
 
int main()
{
 
//Création de l'objet NombreUn à l'aide du constructeur par défaut
Nombre NombreUn;
 
//Création de deux objets à l'aide du constructeur surchargé
Nombre NombreDeux = 7;
Nombre NombreTrois = 4;
 
cout << endl;
cout << "Valeur initiale des objets a additionner : " << endl;
cout << "NombreUn = " << NombreUn.get_Val() << endl;
cout << "NombreDeux = " << NombreDeux.get_Val() << endl;
cout << "NombreTrois = " << NombreTrois.get_Val() << endl << endl;
 
cout << "On additionne les objets NombreDeux et NombreTrois : " << endl;
NombreUn = NombreDeux.Ajoute(NombreTrois);
cout << "NombreDeux + NombreTrois = NombreUn Soit : " << endl;
cout << NombreDeux.get_Val() << " + " << NombreTrois.get_Val() << " = " <<
NombreUn.get_Val() << endl;
 
_getch();
return 0;
 
}  

 

   Nous commençons par créer 3 objets nombres dont les deux derniers sont respectivement initialisés à 7 et 4, le premier étant initialisé à zéro par défaut dans son constructeur.

   Remarquez l'affectation :

NombreUn = NombreDeux.Ajoute(NombreTrois) ;

   L'objet NombreUn est l'objet renvoyé par la fonction Ajoute() dont nous avons vu l'implémentation dans le fichier Surcharge_7.h. cheeky

   Ce qui donne dans la console :

 

 

 

      1.2 – Surcharge de l'opérateur d'addition operator+

 

   Nous allons voir qu'il est bien plus parlant d'additionner deux objets en écrivant :

NombreUn = NombreDeux + NombreTrois;

   plutôt que :

NombreUn = Nombre2.Ajoute(NombreTrois);

   Il nous suffit, comme nous l'avons vu dans le chapitre précédent, de créer une fonction operator, suivie de l'opérateur + à surcharger et contenant un paramètre à l'intérieur des parenthèses.

 

Remarque : Dans une fonction de surcharge d'opérateur, l'argument passé à l'intérieur des parenthèses est une des valeurs se trouvant à droite du signe =, soit un objet de la classe soit une valeur numérique.


   Exemple :

nombreUn = nombreDeux + nombreTrois;

   ou

nombreUn = nombreDeux + 10;

   La forme de déclaration de la fonction sera dans le premier cas :

Nombre operator+ (const Nombre &rhs);

   Et dans le second cas :

Nombre operator+ (const type_argument &rhs);

 

   Voyons ceci dans l'exemple suivant :

   Exemple : Projet 115Surcharge_8

      Fichier Surcharge_8.h  

 

//projet 115Surcharge_8
//Fichier : Surcharge_8.h
//Déclaration de la classe Nombre
//Surcharge de l'opérateur binaire "+"
 
#ifndef DEF_NOMBRE
#define DEF_NOMBRE
 
typedef unsigned uint;
 
class Nombre
{
public:
 
//Constructeurs
Nombre() : _valeur(0) {}
Nombre(uint valeur) : _valeur(valeur) {}
 
//Destructeur
~Nombre() {}
 
// Fonctions membres d'accès public
uint get_Val() const { return _valeur; };
void set_Val(uint val) { _valeur = val; }
 
//Fonction operator+(const Nombre &rhs)
const Nombre operator+ (const Nombre &rhs) { return Nombre(_valeur + rhs.get_Val()); }
 
//Fonction operator+ (const uint &rhs)
const Nombre operator+ (const uint &rhs)
{
Nombre n;
n._valeur = _valeur + rhs;
return n;
}
 
private:
 
//Donnée membre privée
uint _valeur;
 
};
#endif 

 

    Dans la première fonction operator+(), comparé à l'exemple précédent, seul le nom de la fonction membre d'addition change. Le nom ''operator+'' remplace le nom ''Ajoute'' et nous permettra, comme nous l'avons signalé plus haut, d'écrire l'addition de deux objets de la même façon que l'on additionne deux nombres d'un type arithmétique.

   Dans la seconde fonction operator+(), un objet temporaire est créé dans le corps de la fonction. On ajoute à cet objet la valeur du membre de droite de type uint et on renvoie l'objet dès qu'il sort du domaine de définition de cette fonction.

   Fichier Surcharge_8.MainFile.cpp

 

//projet 115Surcharge_8
//Fichier : Surcharge_8MainFile.cpp
//Implémentation d'une fonction d'addition
//Fonction main()
 
#include <iostream>
#include "Surcharge_8.h"
#include <conio.h>
 
using namespace std;
 
 
int main()
{
//Création de l'objet NombreUn à l'aide du constructeur par défaut
Nombre NombreUn;
 
//Création de deux objets à l'aide du constructeur surchargé
Nombre NombreDeux = 7;
Nombre NombreTrois = 4;
 
cout << endl;
cout << "Valeur initiale des objets a additionner : " << endl;
cout << "NombreUn = " << NombreUn.get_Val() << endl;
cout << "NombreDeux = " << NombreDeux.get_Val() << endl;
 
cout << "NombreTrois = " << NombreTrois.get_Val() << endl << endl;
 
cout << "On additionne les objets NombreDeux et NombreTrois : " << endl;
 
NombreUn = NombreDeux + NombreTrois;
cout << "NombreDeux + NombreTrois = NombreUn Soit : " << endl;
 
cout << NombreDeux.get_Val() << " + " << NombreTrois.get_Val() << " = " <<
NombreUn.get_Val() << endl << endl;
 
cout << "On additionne la valeur 10 a l'objet NombreDeux : " << endl;
NombreUn = NombreDeux + 10;
cout << "NombreUn = NombreDeux + 10 Soit : " << endl;
cout << NombreDeux.get_Val() << " + " << 10 << " = " << NombreUn.get_Val();
 
_getch();
return 0;
} 

 

   Et dans le fichier principal, nous nous apercevons donc que l'addition de deux objets se fait par l'assignation NombreUn = NombreDeux + NombreTrois, ce qui est quand-même bien plus parlant que l'instruction NombreUn = NombreDeux.Ajoute(NombreTrois) que nous avons utilisée dans l'exemple 114Surcharge_7 d'implémentation d'une fonction d'addition. wink Nous voyons en outre que la seconde fonction const Nombre operator+ (const uint &rhs) permet d'additionner une valeur numérique à un objet nombreDeuxsmiley

   Ce qui donne comme résultat dans la console :

 

 

 

      1.3 – Utiliser une fonction amie pour surcharger l'opérateur d'addition operator+

 

   Dans le chapitre 18, nous avons vu que nous pouvions utiliser une fonction amie pour surcharger les opérateurs ''++'' et ''--'' et il en va de même avec les opérateurs ''+'' et ''-''. Vous devez simplement vous souvenir que les fonctions amies n'ayant pas d'accès automatique aux éléments des données de la classe, deux arguments doivent leur être explicitement passés. wink

   Le second paramètre devant être une valeur numérique, nous allons, pour notre classe Nombre, écrire la fonction sous cette forme :

friend Nombre operator+ (const Nombre &, size_t);

   Le premier paramètre étant une référence constante sur un objet de type Nombre et le second une valeur de type size_t, par exemple.

   Voyons ceci avec un nouvel exemple.

   Exemple : Projet 116Surcharge_9

      Fichier Surcharge_9.h    

 

//projet 116Surcharge_9
//Fichier : Surcharge_9.h
//Déclaration de la classe Nombre
//Utilisation d'une fonction amie pour la surcharge de l'opérateur "+"
 
#ifndef DEF_NOMBRE
#define DEF_NOMBRE
 
 
class Nombre
{
public:
 
//Constructeurs
Nombre() : _valeur(0) {}
Nombre(size_t valeur) : _valeur(valeur) {}
 
//Destructeur
~Nombre() {}
 
// Fonctions membres d'accès public
size_t get_Val() const { return _valeur; };
void set_Val(size_t val) { _valeur = val; }
 
//Fonction amie operator+()
friend Nombre operator+ (const Nombre &rhs, size_t val)
{
Nombre a;
a._valeur = val + rhs._valeur;
return a;
}
 
//Fonction amie operator+(size_t, const Nombre &rhs)
friend Nombre operator+ (size_t val, const Nombre &rhs)
{
Nombre a;
a._valeur = val + rhs._valeur;
return a;
}
 
private:
//Donnée membre privée
size_t _valeur;
 
};
#endif 

 

   La fonction operator+() doit explicitement renvoyer une valeur (un membre de la classe) et cette valeur doit être différente de chacun des arguments passés à la fonction car ceux-ci ne doivent pas eux-mêmes être modifiés par l'opérateur d'addition.

   Nous créons donc un objet ''Nombre'' temporaire (a) dans le corps de la fonction et celui-ci est renvoyé dès qu'il sort de son domaine de visibilité (quand la fonction se termine). wink

   Fichier Surcharge_9.MainFile.cpp

 

//projet 116Surcharge_9
//Fichier : Surcharge_9MainFile.cpp
//Déclaration de la classe Nombre
//Utilisation d'une fonction amie pour la surcharge de l'opérateur "+"
 
#include <iostream>
#include "Surcharge_9.h"
#include <conio.h>
 
using namespace std;
 
 
int main()
{
 
//Création de l'objet NombreUn à l'aide du constructeur par défaut
Nombre nombreUn;
 
//Création d'un objet à l'aide du constructeur surchargé
Nombre nombreDeux = 7;
 
//On initialise une variable avec une valeur de type size_t
uint nombreTrois = 3;
 
cout << endl;
cout << "Valeur initiale de deux objets a additionner : " << endl;
cout << "nombreUn = " << nombreUn.get_Val() << endl;
cout << "nombreDeux = " << nombreDeux.get_Val() << endl << endl;
cout << "nombreTrois est une variable de type size_t : " << endl;
cout << "nombreTrois = " << nombreTrois << endl << endl;
 
cout << "On additionne l'objet NombreDeux a la valeur size_t nombreTrois : " << endl;
nombreUn = nombreDeux + nombreTrois;
cout << "NombreDeux + NombreTrois = NombreUn Soit : " << endl;
cout << nombreDeux.get_Val() << " + " << nombreTrois << " = " << nombreUn.get_Val()
<< endl << endl;
 
cout << "On ajoute un objet a une valeur numerique :" << endl;
nombreUn = 10 + nombreDeux;
cout << "10 + nombreDeux = nombreUn soit : " << endl;
cout << 10 << " + " << nombreDeux.get_Val() << " = " << nombreUn.get_Val();
 
_getch();
return 0;
 
} 

 

     Dans ce fichier, la différence avec le fichier Surcharge_8MainFile.cpp de l'exemple précédent est qu'à la suite de la création de deux objets nombreUn et nombreDeux, nous initialisons une variable nombreTrois de type size_t. On additionne alors l'objet nombreDeux et la variable nombreTrois et on assigne le résultat à l'objet nombreUn.

   Si nous ajoutons une valeur numérique à un objet Nombre, il ne faut pas oublier que c'est une valeur de type size_t qui est passée au constructeur surchargé de la classe Nombre. C'est donc bien des valeurs de même type qui sont additionnés.

   Ce qui donne comme résultat dans la console :
 

 

 

Attention : les membres de l'addition doivent être écrits dans l'ordre des paramètres passés à la fonction operator+(). En effet on peut ajouter une quelconque valeur arithmétique à la suite de l'assignation nombreUn = nombreDeux + nombreTrois;

 

   Par exemple : nombreUn = nombreDeux + nombreTrois + 10; peut être assigné par l'intermédiaire de la fonction friend Nombre operator+ (const Nombre &rhs, size_t val);

   Mais l'assignation :

nombreUn = 10 + nombreDeux + nombreTrois;

   ne peut se faire que par l'intermédiaire de la fonction friend Nombre operator+ (size_t val, const Nombre &rhs);

   Nous avons donc dû définir une seconde fonction amie en intervertissant les paramètres. wink

   Et nous ajouterons qu'une assignation du type ''nombreUn = nombreDeux + nombreTrois'' peut également s'écrire nombreUn = operator+ (nombreDeux, nombreTrois).


   Ces remarques étant faites, vous devez aussi vous douter que l'implémentation d'une fonction de soustraction operator- () se fait de la même manière que pour l'opérateur operator+wink

   Et ceci nous conduit à notre premier exercice :

 

      Exercice 1 :

 

   En vous basant sur le projet 115Surcharge_8, créez une classe Nombre avec ces deux fonctions membres :

Nombre operator+ (const Nombre &rhs);

Nombre operator- (const Nombre &rhs);

   ainsi qu'un constructeur surchargé auquel vous passerez une variable d'un type arithmétique quelconque.

   Dans la fonction main() vous additionnerez deux objets par l'intermédiaire de la fonction operator+() et afficherez leur somme. Incrémentez un des deux objets à l'aide de la fonction operatot++() vue dans le chapitre précédent et soustrayez ensuite ces objets par l'intermédiaire de la fonction operator-(). Faites le test que l'on soustrait bien le plus petit du plus grand et affichez leur différence. Et ne copiez pas le voisin hein... cheeky Je vous ai à l'oeil. angry

 

 

      1.4 – Surcharge de l'opérateur d'affectation operator=

 

   Dans les projets 115Surcharge_8 et 116Surcharge_9, vous aurez remarqué que nous n'avons pas eu besoin de surcharger l'opérateur = pour que l'affectation de plusieurs objets à un autre se fasse sans grincement de dents du compilateur. indecision En effet, l'opérateur d'affectation se trouvant déjà à l'intérieur des fonctions operator+(), c'est l'opérateur non surchargé qui a été utilisé par le compilateur. wink

   Et c'est pourquoi l'affectation :

NombreUn = NombreDeux + NombreTrois;

   a bien donné la valeur attendue comme résultat. smiley

   Bien entendu, l'opérateur = peut également être surchargé et nous allons voir dans un prochain exemple qu'il est même important d'en implémenter un. En attendant, sa déclaration pour notre classe Nombre serait de la forme :

Nombre operator= (Nombre &rhs);

   L'implémentation de cette fonction est la suivante : 

 

Nombre operator= (const Nombre &rhs)
{
_valeur = rhs._valeur;
return *this;
} 

 

      De même que pour les autres opérateurs binaires dont la surcharge est réalisée au moyen de fonctions membres, on ne passe qu'une seule valeur explicitement à la fonction. Il s'agit d'une référence de la valeur située à droite (&rhs) du membre surchargé. La valeur située à gauche de l'opérateur est le pointeur implicite thiswink

   L'opérateur = devant rendre égale la valeur de l'argument de gauche à la valeur de l'argument de droite, ceci se fait en renvoyant l'objet courant pointé par this. Celui-ci étant un pointeur et non une valeur nous retournons donc le pointeur déréférencé *this.


   Quand implémenter une fonction de surcharge de l'opérateur ''='' ?

 

   Dans le chapitre 16, § 4.2, nous avons vu, en ce qui concerne le constructeur de copie, qu'il était nécessaire, lors de la déclaration de variables privées en tant que pointeurs, de réaliser ce qu'on appelle une copie en profondeur en implémentant notre propre constructeur de copie.

   Supposons que nous ayons à créer un objet d'une classe représentant par exemple une potion de vie (vial en anglais). Si, dans notre classe ''Vial'', comprend des pointeurs en tant que membres privés, et que nous tentons ultérieurement d'affecter un objet Vial à un autre identique, nous risquons alors, dans le cas d'un projet plus élaboré, d'avoir le même problème de fuite de mémoire que celui rencontré avec un constructeur de copie de surface. angry

   En effet, l'affectation d'un objet unVial telle unVial = unVial est une opération qui pourrait être le résultat d'une erreur accidentelle. blush

   Que va-t-il se passer ? surprise

   L'objet unVial va libérer l'espace occupé en mémoire mais le problème est que dès que l'objet sera en mesure de recevoir les valeurs de la partie droite de l'affectation, elles auront disparu !? indecision

   Une solution de sécurité est d'implémenter une fonction de surcharge d'opérateur d'affectation operator=() qui testera que la partie droite de l'opérateur d'affectation est bien l'objet lui-même en examinant la valeur du pointeur this.

   Nous voyons ceci dans l'exemple suivant où nous ajouterons un fichier de définition :


   Exemple : Projet 117Surcharge_10

      Fichier Surcharge_10.h  

  

//projet 117Surcharge_10
//Fichier : Surcharge_10.h
//Déclaration de la classe Vial
//Implémentation d'une fonction operator=()
 
#ifndef DEF_VIAL
#define DEF_VIAL
 
#include <iostream>
#include <string>
 
 
class Vial
{
public:
 
//Constructeur par défaut
Vial();
 
//Constructeur de copie
Vial(const Vial &rhs);
 
//Destructeur
~Vial();
 
//Fonctions membres d'accès publique
//Accesseurs
std::string get_Name() const;
size_t get_PA() const;
size_t get_PV() const;
size_t get_Regeneration() const;
 
 
//Mutateurs
void set_Name(std::string nom);
void set_PA(size_t prix_Achat);
void set_PV(size_t prix_Revente);
void set_Regeneration(size_t regeneration);
 
//Fonction operator=()
Vial operator= (const Vial &rhs);
 
 
private:
 
//Données membres privées
std::string *_nom;
size_t *_prix_Achat;
size_t *_prix_Revente;
size_t *_regeneration;
 
};
#endif 

 

   Rien de spécial dans ce fichier, notez simplement la déclaration d'une fonction operator=() et de données membres privées sous forme de pointeurs.

   Fichier Surcharge_10.cpp 

 

//projet 117Surcharge_10
//Fichier : Surcharge_10.cpp
//Définition de la classe Vial
//Implémentation d'une fonction operator=()
 
#include "Surcharge_10.h"
 
using namespace std;
 
//Implémentation du constructeur par défaut
Vial::Vial()
{
_nom = new std::string;
_prix_Achat = new size_t;
_prix_Revente = new size_t;
_regeneration = new size_t;
 
*_nom = "Petite potion de regeneration";
*_prix_Achat = 100;
*_prix_Revente = 10;
*_regeneration = 30;
}
 
//Implémentation du constructeur de copie
Vial::Vial(const Vial &rhs)
{
_nom = new std::string;
_prix_Achat = new size_t;
_prix_Revente = new size_t;
_regeneration = new size_t;
 
*_nom = rhs.get_Name();
*_prix_Achat = rhs.get_PA();
*_prix_Revente = rhs.get_PV;
 
*_regeneration = rhs.get_Regeneration();
}
 
//Implémentation du destructeur
 
Vial::~Vial()
{
*_nom = nullptr;
delete _nom;
*_prix_Achat = 0;
delete _prix_Achat;
*_prix_Revente = 0;
delete _prix_Revente;
*_regeneration = 0;
delete _regeneration;
}
 
//Implémentation des fonctions membres d'acqusition
string Vial::get_Name() const { return *_nom; }
size_t Vial:: get_PA() const { return *_prix_Achat; }
size_t Vial::get_PV() const { return *_prix_Revente; }
size_t Vial:: get_Regeneration() const { return *_regeneration; }
 
//Implémentation des fonctions membres d'assignation
void Vial::set_Name(std::string nom) { *_nom = nom; }
void Vial::set_PA(size_t prix_Achat) { *_prix_Achat = prix_Achat; }
void Vial::set_PV(size_t prix_Revente) { *_prix_Revente = prix_Revente; }
void Vial::set_Regeneration(size_t regeneration) { *_regeneration = regeneration; }
 
//Implémentation de la fonction operator=()
Vial Vial::operator= (const Vial &rhs)
{
if (this == &rhs)
return *this;
 
else
{
*_nom = rhs.get_Name();
*_prix_Achat = rhs.get_PA();
*_prix_Revente = rhs.get_PV();
*_regeneration = rhs.get_Regeneration();
return *this;
}
} 

 

     Dans le fichier Surcharge_10.cpp, nous implémentons les constructeurs, les différentes fonctions membres ainsi que la fonction operator=(). Comme signalé plus haut, c'est dans cette fonction que nous allons comparer les deux membres de l'affectation.

   Si l'adresse de l'objet de droite (rhs) est identique à l'adresse contenue dans le pointeur this, l'affectation ne se fera pas et on renverra l'objet courant (le pointeur déréférencé *this). wink

   Si les deux membres sont différents, l'affectation peut se faire, les membres de l'objet de droite sont copiés dans l'objet de gauche et celui-ci est alors renvoyé. angel

   Fichier Surcharge_10.MainFile.cpp

 

//projet 117Surcharge_10
//Fichier : Surcharge_10MainFile.cpp
//Définition de la classe Vial
//Implémentation d'une fonction operator=()
 
#include <iostream>
#include "Surcharge_10.h"
#include <conio.h>
 
using namespace std;
 
int main()
{
//Création de l'objet NombreUn à l'aide du constructeur par défaut
Vial unVial;
 
//Création d'un second objet
Vial secondVial;
 
//On affiche les membres de l'objet unVial
cout << endl;
cout << "Membres de l'objet unVial : " << endl;
cout << "Nom : " << unVial.get_Name() << endl;
cout << "Prix d'achat : " << unVial.get_PA() << endl;
cout << "Prix de revente : " << unVial.get_PV() << endl;
cout << "Points de vie regeneres : " << unVial.get_Regeneration() << endl << endl;
 
cout << endl;
cout << "Membres de l'objet seconVial : " << endl;
cout << "Nom : " << secondVial.get_Name() << endl;
cout << "Prix d'achat : " << secondVial.get_PA() << endl;
cout << "Prix de revente : " << secondVial.get_PV() << endl;
cout << "Points de vie regeneres : " << secondVial.get_Regeneration() << endl << endl;
 
//On augmente la quantité gégénéréé et on diminue le prix de l'objet unVial
cout << "On augmente la quantite regeneree et on diminue le prix de l'objet unVial :" << endl;
unVial.set_Regeneration(40);
unVial.set_PA(90);
unVial.set_PV(9);
cout << "Points de vie regeneres : " << unVial.get_Regeneration() << endl;
cout << "Prix du vial : " << unVial.get_PA() << endl << endl;
 
//On affecte l'objet unVial à l'objet secondVial (constructeur de copie)
secondVial = unVial;
cout << "On affecte l'objet unVial a l'objet secondVial" << endl;
cout << "Membres de l'objet seconVial : " << endl;
cout << "Nom : " << secondVial.get_Name() << endl;
cout << "Prix d'achat : " << secondVial.get_PA() << endl;
cout << "Prix de revente : " << secondVial.get_PV() << endl;
cout << "Points de vie regeneres : " << secondVial.get_Regeneration() << endl << endl;
 
//Maintenant on affecte secondVial à lui-même
secondVial = secondVial;
cout << "Maintenant on affecte secondVial a lui-meme" << endl;
cout << "Membres de l'objet seconVial : " << endl;
cout << "Nom : " << secondVial.get_Name() << endl;
cout << "Prix d'achat : " << secondVial.get_PA() << endl;
cout << "Prix de revente : " << secondVial.get_PV() << endl;
cout << "Points de vie regeneres : " << secondVial.get_Regeneration() << endl;
 
_getch();
return 0;
} 

 

     Nous créons deux objets, unVial et secondVial. Le contenu des membres de chaque objet est ensuite affiché et par la suite nous modifions les valeurs contenues dans les adresses pointées par *prix_Achat, *prix_Revente et *_regeneration de l'objet unVial. On affecte alors l'objet unVial à l'objet secondVial et ce, par l'intermédiaire du constructeur de copie.

   Pour terminer on affecte secondVial à lui-même. Nous utilisons ici la fonction operator=() qui teste l'égalité ou non des deux objets. Notez que lors de l'affectation de unVial à secondVial, la fonction operator=() avait également fait ce test. wink

   Maintenant que nous voulons affecter un objet à un autre identique, que va-t-il se passer ? Nous avons vu que le pointeur this allait alors nous renseigner sur l'identité de l'objet situé à droite du signe =. Les deux objets étant tout-à-fait similaires, l'affectation de chaque membre de l'objet pointé par this ne se fait pas et nous évitons peut-être un ''accident'' à notre programme. Ouf ! angel

 

   Notez que j'ai également compilé ce programme sans la fonction operator=(), donc sans effectuer de test sur la similitude des deux objets et que je n'ai eu aucune mauvaise surprise.


   Quoi qu'il en soit, ajouter ce test dans une fonction de surcharge de l'opérateur = ne mange pas de pain et vous assurera qu'un programme plus conséquent pourra se dérouler sans crainte d'un accident qui pourrait éventuellement le bloquer. frown

   Et ceci nous affiche dans la console :

 

 

 

   1.5 – Surcharge des opérateurs binaires ''+='' et ''- =''


   Nous allons donner un exemple de surcharge des opérateurs d'affectation operator+= et operator-= dans notre classe Nombre.

   Notez que les opérateurs += et - = sont des opérateurs uniques et non des mélanges d'opérateurs +, - et =.

   Pour notre classe Nombre, les déclarations des fonctions de surcharge operator+=() et operator- =() s'écriront :

void operator+= (type_argument)

   et

void operator- =(type_argument)

   Le type de l'argument passé à l'intérieur des parenthèses étant un des types définis par le langage. wink


   Exemple : Projet 118Surcharge_11

      Fichier Surcharge_11.h  

 

//projet 118Surcharge_11
//Fichier : Surcharge_11.h
//Déclaration de la classe Nombre
//Surcharge des opérateurs "+=" et "-="
 
#ifndef DEF_NOMBRE
#define DEF_NOMBRE
 
 
class Nombre
{
public:
 
//Constructeurs
Nombre() : _valeur(0) {}
Nombre(size_t valeur) : _valeur(valeur) {}
 
//Destructeur
~Nombre() {}
 
// Fonctions membres d'accès public
size_t get_Val() const { return _valeur; };
void set_Val(size_t val) { _valeur = val; }
 
//Fonction operator+= (size_t val)
void operator+= (size_t val) { _valeur += val; }
 
//Fonction operator-= (size_t val)
void operator-= (size_t val) { _valeur -= val; }
 
 
private:
 
//Donnée membre privée
size_t _valeur;
 
};
#endif 

 

     Il pourrait sembler étonnant que l'on puisse utiliser les opérateurs += et - = à l'intérieur même des fonctions operator+=() et operator-=() qui sont utilisées pour les surcharger ? frown

   Mais dans le corps de ces fonctions, les instructions _valeur += val et _valeur -= val utilisent ces opérateurs dans leur sens d'origine, c'est-à-dire qu'ils ajoutent ou soustraient la valeur de la variable de type size_t val à un élément de même type, _valeur.

 

   Fichier Surcharge_11.MainFile.cpp 

 

//projet 118Surcharge_11
//Fichier : Surcharge_11MainFile.cpp
//Déclaration de la classe Nombre
 
//Surcharge des opérateurs "+=" et "-="
 
#include <iostream>
#include "Surcharge_10.h"
#include <conio.h>
 
using namespace std;
 
 
int main()
{
//Création d'un objet à l'aide du constructeur surchargé
Nombre unNombre = 7;
 
//On initialise une variable avec une valeur de type size_t
size_t val = 3;
cout << endl;
 
cout << "Objet cree : unNombre = " << unNombre.get_Val() << endl;
cout << "Valeur de la variable val = " << val << endl << endl;
 
cout << "On assigne la valeur val a l'objet unNombre : " << endl;
cout << "unNombre += " << val << endl;
unNombre += val;
cout << "unNombre = " << unNombre.get_Val() << endl << endl;
 
cout << "Maintenant nous allons soustraire val de unNombre : " << endl;
 
if (val <= unNombre.get_Val())
{
unNombre -= val;
cout << "unNombre -= " << val << endl;
cout << "unNombre = " << unNombre.get_Val() << endl;
}
 
else
cout << "Soustraction non autorisee !" << endl;
 
_getch();
return 0;
} 

 

     Et il n'y a rien de spécial à ajouter à la fonction main(), celle-ci étant parfaitement compréhensible. Ceci nous donne dans la console :

 

 

 

   1.6 – Utiliser des fonctions amies pour surcharger les opérateurs ''+='' et ''- =''

 

   Les opérateurs += et - = peuvent également être surchargés en utilisant les fonctions amies au lieu de fonctions membres.

   Nous savons maintenant que les fonction amies utilisées pour la surcharge d'opérateurs prennent deux arguments au lieu d'un, donc pour notre classe Nombre, les déclarations des fonctions de surcharge operator+=() et operator-=() s'écriront:

friend void operator+= (Nombre &lhs, size_t );

   et

friend void operator-= (Nombre &lhs, size_t)

   Heu... frown tu n'aurais pas dû écrire &rhs à la place de &lhs ? surprise

   Et bien non, car nous utilisons ici le terme anglo-saxon lhs qui veut dire ''left-hand-side'' ou ''côté-main-gauche'' en français. En effet, nous allons voir dans l'exemple suivant que la référence sur un objet ''Nombre'' se trouve bien à gauche des opérateurs += ou - =.

 

   Exemple : Projet 119Surcharge_12

      Fichier Surcharge_12.h   

 

//projet 119Surcharge_12
//Fichier : Surcharge_12.h
//Déclaration de la classe Nombre
//Surcharge des opérateurs "+=" et "-=" à l'aide de fonctions amies
 
#ifndef DEF_NOMBRE
#define DEF_NOMBRE
 
 
class Nombre
{
public:
 
//Constructeurs
Nombre() : _valeur(0) {}
 
Nombre(size_t valeur) : _valeur(valeur) {}
//Destructeur
~Nombre() {}
 
// Fonctions membres d'accès public
size_t get_Val() const { return _valeur; };
void set_Val(size_t val) { _valeur = val; }
 
//Fonction friend void operator+= (Nombre &lhs, size_t val)
friend void operator+= (Nombre &lhs, size_t val) { lhs._valeur += val; }
 
//Fonction friend void operator-= (Nombre &lhs, size_t val)
friend void operator-= (Nombre &lhs, size_t val) { lhs._valeur -= val; }
 
 
private:
 
//Donnée membre privée
size_t _valeur;
 
};
#endif 

 

    La valeur de la variable ''val'' doit être ajoutée ou soustraite d'une référence sur un objet Nombre.

   Celui-ci se trouve bien à gauche des opérateurs += et - =.

   Et cet objet devant être modifié, il nous est donc interdit de le faire précéder du qualificateur const dans la liste des paramètres. smiley

 

   Fichier Surcharge_12MainFile.cpp

   Ce fichier étant à l'identique du fichier Surcharge_11MainFile.cpp ainsi que le résultat dans la console, ils ne seront donc pas reproduits ici.

 


      Exercice 2 (un petit challenge)

 

   Ecrivez un programme qui affiche le résultat (positif) de l'expression suivante :

objet3 = 10 + objet1 + 20 + objet2 – 15;

   Chaque opérateur de l'expression devra être un opérateur surchargé. Attention, comme nous n'avons pas encore vu la surcharge de l'opérateur <<, le compilateur vous refusera l'instruction ''cout << objet3''. Vous utiliserez à la place une fonction membre Affiche().

   Pour cet exercice vous créerez un fichier de déclaration, un fichier de définition et un fichier mainFile.

 


   2 – Corrigés des exercices du chapitre 18

      Exercice 1

 

   Pour cet exercice on demandait de décrémenter 1 objet ''unNombre'' créé à partir d'une classe Compteur. Vous afficherez 20 premiers objets de 20 à 1 à l'aide de l'opérateur operator--.

   Voici un des codes possibles :

       Fichier Chap18Ex_1.h  

 

//projet Chap18Exercice_1
//Fichier : Chap18Ex_1.h
//Déclaration de la classe Compteur
//Surcharge de l'opérateur de pré-décrémentation operator--
#ifndef DEF_COMPTEUR
#define DEF_COMPTEUR
 
 
class Compteur
{
public:
 
// Constructeur
Compteur() { _nb = 21; }
 
// Destructeur
~Compteur() {}
 
// Fonction membre d'accès public
int get_Nombre() const { return _nb; };
 
//Surcharge de l'opérateur --
void operator--() { --_nb; }
 
 
private:
 
//Donnée membre privée
int _nb;
 
};
#endif 

 

   L'opérateur étant un opérateur de pré-décrémentation, on donne la valeur 21 à la variable privée _nb dans le constructeur. En effet, la valeur sera d'abord décrémentée avant d'être affichée. wink

   Fichier Chap18Ex_1.cpp

 

//projet Chap18Exercice_1
//Fichier : Chap18Ex_1.cpp
//Fonction main()
//Surcharge de l'opérateur de pré-décrémentation operator--
 
#include <iostream>
#include "Chap18Ex_1.h"
#include <conio.h>
 
using namespace std;
 
 
int main()
{
//Création de l'objet unNombre
Compteur unNombre;
 
//Initialisation d'un compteur à 0;
int ctr(0);
 
//Décrémentation d'un objet de type Compteur
cout << endl;
 
cout << "Surcharge de l'operateur 'operator--'" << endl << endl;
while (ctr != 20)
{
//Décrémentation de l'objet unNombre à l'aide
//de l'opérateur -- surchargé.
--unNombre;
cout << unNombre.get_Nombre() << " ";
++ctr;
}
 
_getch();
return 0;
} 

 

    Nous créons donc un objet unNombre ainsi qu'une variable ctr initialisée à zero. Dans une boucle while, on décrémente l'objet 20 fois, le compteur ctr étant incrémenté de 0 à 19. Donc rien de compliqué dans cet exercice.

   Vous deviez avoir ce résultat dans la console :

 

 

 

   Exercice 2 (un petit challenge)

 

   Pour cet exercice il était demandé : En vous basant sur le projet 112Surcharge_5, créez une classe ''Nombre''. Cette classe comprendra un constructeur par défaut ainsi qu'une fonction amie friend void operator++(Nombre&).

 

   Dans la fonction main(), créez un objet unNombre à l'aide du constructeur par défaut et un objet autreNombre à l'aide du constructeur de copie par défaut. La fonction operator++() devra calculer le double du carré des nombres de 1 à 20.

 

   Voici une solution possible :

 

      Fichier Chap18Ex_2.h  

 

 

//projet Chap18Exercice_2
//Fichier : Chap18Ex_2.h
//Déclaration de la classe Nombre
//Implémentation d'une fonction amie operator++(Nombre &)
#ifndef DEF_OPERATION
#define DEF_OPERATION
 
using ULLong = unsigned long long;
 
 
class Nombre
{
 
public:
 
// Constructeur par defaut
Nombre()
{
valeur = 1;
resultat = 0;
}
 
 
// Fonction amie operator++()
friend void operator++(Nombre &param)
{
param.resultat = 2 * (param.valeur * param.valeur);
++param.valeur;
}
 
// Fonction d'affichage
void Affiche() { std::cout << resultat; }
 
 
private:
//Données membres privées
ULLong resultat;
ULLong valeur;
 
};
#endif 

 

    Le constructeur par défaut initialise une variable ''valeur'', qui est la première valeur des nombres de 1 à 20 ainsi qu'une variable résultat à zéro qui sera le résultat de chaque opération réalisée pour un nombre.

   La fonction operator++() réalise l'opération demandée pour chaque nombre incrémenté de 1 à 20.


   Fichier Chap18Ex_2.cpp 

 

//projet 109Surcharge_2
//Fichier : Surcharge_2.cpp
//Fonction main()
//Surcharge de l'opérateur de pré-incrémentation ++
 
#include <iostream>
#include "Chap18Ex_2.h"
 
#include <conio.h>
 
using namespace std;
 
 
int main()
{
// Création d'un objet avec le constructeur par défaut
Nombre unNombre;
 
// On crée un second objet avec le constructeur de copie
Nombre autreNombre(unNombre);
 
// Maximum de nombres à afficher
const ULLong maxNombre = 20;
 
cout << endl;
 
for (auto ctr = 1; ctr != maxNombre + 1; ++ctr)
{
// operator++ incrémente l'objet unNombre
++unNombre;
unNombre.Affiche();
cout << " ";
}
 
cout << endl;
 
for (auto ctr = 1; ctr != maxNombre + 1; ++ctr)
{
// operator++ incrémente l'objet autreNombre
++autreNombre;
autreNombre.Affiche();
cout << " ";
}
 
_getch();
return 0;
} 

 

   Dans la fonction main(), l'objet unNombre est créé a l'aide du constructeur par défaut et l'objet autreNombre à l'aide du constructeur de copie par défaut.

   Suivent deux boucles qui incrémentent les objets et les affichent par l'intermédiaire d'une fonction membre Affiche().

   Donc rien de difficile dans cet exercice.

 

   Voilà, je crois que c'en est assez pour ce chapitre. Dans le chapitre 20 nous verrons la surcharge des opérateurs d'entrées/sorties, la surcharge des opérateurs d'égalité/inégalité ainsi que quelques relations entre objets, références et pointeurs dans le chapitre 21.  cool

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

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

            Gondulzak.  angel
 
 
 

Connexion

CoalaWeb Traffic

Today94
Yesterday297
This week891
This month4565
Total1743772

26/04/24