Une des fonctionnalités les plus étonnantes du C++ est la surcharge d'opérateurs. C'est une technique qui permet de réaliser des opérations mathématiques intelligentes entre vos objets. Vous allez voir que votre code sera plus court, plus clair, et donc plus lisible.
Comprenez le principe de surcharge d'opérateurs
Imaginons : vous avez créé une classe pour stocker une durée (par exemple 4 h 21 min), et vous avez deux objets de type Duree
que vous voulez additionner pour connaître la durée totale.
En temps normal, il faudrait créer une fonction :
Duree resultat, duree1, duree2;
resultat = additionner(duree1, duree2);
La fonction additionner
réaliserait ici la somme de duree1
et duree2
, puis stockerait la valeur ainsi obtenue dans resultat
. Cela fonctionne, mais ce n'est pas franchement lisible.
Je vous propose de modifier la classe Duree
de sorte de pouvoir écrire cela :
Duree resultat, duree1, duree2;
resultat = duree1 + duree2;
Créez la classe Duree
Toutes les classes ne sont pas forcément adaptées à la surcharge d'opérateurs. Ainsi, additionner des objets de type Personnage
serait pour le moins inutile. Nous allons donc changer d'exemple, ce sera l'occasion de vous aérer un peu l'esprit.
Cette classe Duree
sera capable de stocker des heures, des minutes et des secondes.
Le fichier Duree.hpp
#ifndef DEF_DUREE
#define DEF_DUREE
class Duree
{
public:
Duree(int heures = 0, int minutes = 0, int secondes = 0);
private:
int m_heures;
int m_minutes;
int m_secondes;
};
#endif
Chaque objet de type Duree
stockera un certain nombre d'heures, de minutes et de secondes.
J'ai utilisé des valeurs par défaut au cas où l'utilisateur aurait la flemme de les préciser. On pourra donc créer un objet de plusieurs façons différentes :
Duree chrono; // Pour stocker 0 heure, 0 minute et 0 seconde
Duree chrono(5); // Pour stocker 5 heures, 0 minute et 0 seconde
Duree chrono(5, 30); // Pour stocker 5 heures, 30 minutes et 0 seconde
Duree chrono(0, 12, 55); // Pour stocker 0 heure, 12 minutes et 55 secondes
Le fichier Duree.cpp
L'implémentation de notre constructeur est expédiée en 30 secondes, montre en main.
#include "Duree.hpp"
Duree::Duree(int heures, int minutes, int secondes) : m_heures(heures), m_minutes(minutes), m_secondes(secondes)
{
}
Le fichier main.cpp
Pour l'instant, notre main.cpp
ne déclare que deux objets de type Duree
, que j'initialise un peu au hasard :
int main()
{
Duree duree1(0, 10, 28), duree2(0, 15, 2);
return 0;
}
Voilà, nous sommes maintenant prêts à affronter les surcharges d'opérateurs !
Nous allons commencer par voir comment ajouter les opérateurs de comparaison (==, !=, <, >=,...) à notre classe. Ce sont généralement les plus simples à implémenter.
Utilisez les opérateurs de comparaison
Commençons par l'opérateur de test d'égalité : ==
.
Pour être capable d'utiliser le symbole ==
entre deux objets, vous devez créer une fonction ayant précisément pour nom operator==
, dotée du prototype :
bool operator==(Objet const& a, Objet const& b);
La fonction reçoit deux références sur les objets à comparer (références constantes, qu'on ne peut donc pas modifier), et va renvoyer un booléen indiquant si les deux objets sont identiques ou non.
À côté de notre classe Duree
, on doit donc rajouter cette fonction (ici dans le .hpp
) :
bool operator==(Duree const& a, Duree const& b);
Cependant, on aimerait bien que les fonctions ou méthodes ne modifient pas l'objet reçu. C'est pour cela que l'on utilise une référence constante. Quand on effectue le test mathématique , et ne doivent pas être modifiés. Le mot-clé const
est donc essentiel ici.
Comment marche ce truc ?
Dès le moment où vous avez créé cette fonction operator==
, vous pouvez comparer deux objets de type Duree
:
if(duree1 == duree2)
{
std::cout << "Les deux durees sont egales !" << std::endl;
}
De la même manière que l'on aurait pu écrire pour des entiers :
int a(3), b(5);
if(a == b)
{
std::cout << "Les deux entiers sont egaux !" << std::endl;
}
Ce n'est pas de la magie. En fait, le compilateur traduit cela par :
if(operator==(duree1, duree2))
{
std::cout << "Les deux durees sont egales !" << std::endl;
}
C'est bien plus classique et compréhensible pour lui. C'est la nature du mot-clé operator
:
un opérateur se transforme en appel de fonction ;
le compilateur appelle donc la fonction
operator==
en passant en paramètresduree1
etduree2
;la fonction, elle, renvoie un résultat de type
bool
.
Implémentez l'opérateur ==
Pour l'instant, nous avons juste déclaré l'opérateur de comparaison. Il nous faut donc encore expliquer au compilateur comment cette comparaison s'effectue. Dans notre cas c'est simple, deux Duree
sont égales si elles contiennent le même nombre d'heures, de minutes et de secondes. Mais il y a des cas plus compliqués ! Pensez à la classe Personnage
. Deux personnages sont-ils égaux s'ils ont le même nom ? S'ils ont le même nombre de points de vie ? S'ils possèdent la même arme ? C'est au concepteur de la classe de choisir, et de créer son opérateur de comparaison en conséquence.
Revenons à nos durées, et attaquons l'écriture de notre opérateur. La solution la plus simple est de vérifier si les différents attributs de la classe sont égaux. Nous pourrions donc écrire :
bool operator==(Duree const& a, Duree const& b)
{
//Teste si a.m_heure == b.m_heure etc.
if (a.m_heures == b.m_heures && a.m_minutes == b.m_minutes && a.m_secondes == b.m_secondes)
return true;
else
return false;
}
On compare à chaque fois un attribut de l'objet dans lequel on se trouve avec un attribut de l'objet de référence (les heures avec les heures, les minutes avec les minutes…). Si ces 3 valeurs sont identiques, alors on peut considérer que les objets sont identiques, et renvoyer true
.
Sauf qu'il y a un petit souci : il nous faudrait lire les attributs des objets a
et b
. Comme le veut la règle, ils sont privés, et donc inaccessibles depuis l'extérieur de la classe. Il existe trois solutions à ce problème :
Vous créez des accesseurs (ces fameuses méthodes
getHeures()
,getMinutes()
…). Cela marche bien, mais c'est un peu ennuyeux à écrire.Vous utilisez le concept d'amitié, que nous verrons dans un prochain chapitre.
Ou bien vous utilisez la technique que je vais vous montrer.
Alors allons-y !
L'opérateur ==
est situé en dehors de la classe (ce n'est pas une méthode), et ne peut donc accéder aux attributs privés. Nous allons donc créer une méthode dans la classe qui fera la comparaison, et demander à notre opérateur d'appeler cette fonction.
On commence par créer une méthode estEgal()
qui renvoie true
si b
est égal à l'objet dont on a appelé la méthode :
bool Duree::estEgal(Duree const& b) const
{
//Teste si a.m_heure == b.m_heure etc.
if (m_heures == b.m_heures && m_minutes == b.m_minutes && m_secondes == b.m_secondes)
return true;
else
return false;
}
Ou (mieux) en version courte :
bool Duree::estEgal(Duree const& b) const
{
return (m_heures == b.m_heures && m_minutes == b.m_minutes && m_secondes == b.m_secondes); //Teste si a.m_heure == b.m_heure etc.
}
Et on utilise cette méthode dans l'opérateur de comparaison :
bool operator==(Duree const& a, Duree const& b)
{
return a.estEgal(b);
}
Dans le main()
, on peut faire un simple test de comparaison pour vérifier que l'on a fait les choses correctement :
int main()
{
Duree duree1(0, 10, 28), duree2(0, 10, 28);
if (duree1 == duree2)
cout << "Les durees sont identiques";
else
cout << "Les durees sont differentes";
return 0;
}
Résultat :
Les durees sont identiques
Nous avons réussi ! Il est maintenant possible de comparer nos objets de type Duree
de la même manière que nous comparons les entiers.
Je vous propose une vidéo qui reprend la méthode pour surcharger les opérateurs !=
et <
:
Les autres opérateurs de comparaison
Il nous reste encore trois autres opérateurs de comparaison à voir :
>
.<=
.Et
>=
.
Comme pour !=
et ==
, il suffit d'utiliser correctement <
pour tous les implémenter.
Je vous invite à essayer de les implémenter pour notre classe Duree
, cela constituera un bon exercice sur le sujet. Pour vous aider, voici les prototypes :
bool operator>(Duree const &a, Duree const& b)
.bool operator<=(Duree const &a, Duree const& b)
.bool operator>=(Duree const &a, Duree const& b)
.
En résumé
Le C++ permet de surcharger les opérateurs, c'est-à-dire de réaliser des opérations intelligentes entre vos objets, notamment de les comparer.
Pour surcharger un opérateur de comparaison, on doit donner un nom précis à sa méthode (
operator==
pour le symbole==
par exemple).
Vous avez découvert les opérateurs de comparaison mais ce ne sont pas les seuls. Il existe aussi des opérateurs arithmétiques (additions, soustractions, multiplications…) et des opérateurs de flux. Ces derniers sont un peu particuliers, ils agissent sur les flux entrants et sortants. Obscur ? Allez, plongeons dans le prochain chapitre pour y voir plus clair !