Nous allons commencer par voir les opérateurs mathématiques les plus classiques, à savoir l'addition, la soustraction, la multiplication, la division et le modulo.
Une fois que vous aurez appris à vous servir de l'un d'entre eux, vous verrez que vous saurez vous servir de tous les autres.
Pour être capable d'utiliser le symbole "+" entre 2 objets, vous devez créer une fonction ayant précisément pour nom
operator+ qui a pour prototype :
Code : C++ | Objet operator+(Objet const& a, Objet const& b);
|
Même si l'on parle de classe, ceci n'est pas une méthode. C'est une fonction normale située à l'extérieur de toute classe.
La fonction reçoit deux références sur les objets (constantes, donc on ne peut pas les modifier) à additionner.
A coté de notre classe
Duree, on doit donc rajouter cette fonction (ici dans le .h) :
Code : C++ | Duree operator+(Duree const& a, Duree const& b);
|
C'est la première fois que vous utilisez des références constantes. Dans la
sous-partie sur les références, je vous avais expliqué que lors d'un passage par référence, la variable (ou l'objet) n'est pas copié. Notre classe
Duree contient trois entiers, utiliser une référence permet donc d'éviter la copie inutile de ces trois entiers. Ici, le gain est assez négligeable, mais si vous prenez un objet de type
string, il peut contenir un très long texte. La copie prendra alors un temps important. C'est pour cela que lorsque l'on manipule des objets, on préfère utiliser des références. 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 fait

,

et

ne doivent pas être modifiés. Le mot-clé
const est donc essentiel ici.
Mode d'utilisation
Comment ça marche ce truc ?
Dès le moment où vous avez créé cette fonction
operator+, vous pouvez additionner 2 objets de type
Duree entre eux :
Code : C++ | resultat = duree1 + duree2;
|
Ce n'est pas de la magie. En fait le compilateur "traduit" ça par :
Code : C++ | resultat = operator+(duree1, duree2);
|
... ce qui est beaucoup plus classique et compréhensible pour lui

Il appelle donc la fonction
operator+ en passant
duree1 et
duree2 en paramètre. La fonction, elle, va retourner un résultat de type
Duree.
Les opérateurs raccourcis
Ce n'est pas le seul moyen d'effectuer une addition ! Rappelez-vous des versions raccourcies des opérateurs. A côté de
+, il y avait
+= et de même pour les autres. Contrairement à
+ qui est une fonction,
+= est une méthode de la classe. Voici son prototype :
Code : C++ | Duree& operator+=(Duree const& duree);
|
Elle reçoit en argument une autre
Duree à additionner et renvoie une référence sur l'objet lui-même. Nous verrons plus loin à quoi sert cette référence.
Nous voici donc avec deux manières d'effectuer une addition.
Code : C++ | resultat = duree1 + duree2; //Utilisation de operator+
duree1 += duree2; //Utilisation de la méthode operator+= de l'objet duree1
|
Vous vous en doutez peut-être les corps de ces fonctions seront très semblables. Si l'on sait faire le calcul avec
+, il ne faut qu'une petite modification pour obtenir celui de
+= et vice-versa. C'est somme toute deux fois la même opération mathématique.
Les programmeurs sont des fainéants et écrire deux fois la même fonction est vite ennuyeux. C'est pourquoi on va généralement utiliser une de ces deux opérations pour définir l'autre. Et la règle veut que l'on définisse
operator+ en appelant la méthode
operator+=.
Mais comment est-ce possible ?
Prenons un exemple plus simple que des
Duree. Des
int par exemple et analysons ce qui se passe quand on cherche à les additionner.
Code : C++ | int a(4), b(5), c(0);
c = a + b; //c vaut 9
|
On prend la variable
a, on lui ajoute
b et on met le tout dans
c. Ce qui revient presque à écrire :
Code : C++ | int a(4), b(5), c(0);
a += b;
c = a; //c vaut 9 mais a vaut maintenant aussi 9
|
La différence étant que dans ce deuxième exemple, la variable
a a changé de valeur. Si par contre on effectue une copie de
a avant de la modifier, ce problème disparaît.
Code : C++ | int a(4), b(5), c(0);
int copie(a);
copie += b;
c = copie; //c vaut 9 et a vaut toujours 4
|
Le même principe est valable pour * et *=, - et -=, etc.
On peut donc effectuer l'opération
+ en faisant une copie suivi d'un
+=. C'est ce principe que l'on va utiliser pour définir la fonction
operator+ pour notre classe
Duree. Des fois il faut réfléchir beaucoup pour être fainéant.
Code : C++ | Duree operator+(Duree const& a, Duree const& b)
{
Duree copie(a);
copie += b;
return copie;
}
|
Et voilà ! Il ne nous reste plus qu'à définir la méthode
operator+=.
Ce passage est peut-être un peu difficile à saisir au premier abord. L'élément important dont il faut se rappeler c'est la manière dont on écrit la définition de operator+ en utilisant la méthode operator+=. Vous pourrez toujours revenir plus tard sur la justification.
Implémentation de +=
L'implémentation n'est pas vraiment compliquée, mais il va quand même falloir réfléchir un peu. En effet, ajouter des secondes, minutes et heures ça va, mais il faut faire attention à la retenue si ça dépasse 60.
Je vous recommande d'essayer d'écrire la méthode vous-même, c'est un excellent exercice algorithmique, ça entretient le cerveau, ça vous rend meilleur programmeur (je vous ai convaincus là ?

).
Voici ce que donne mon implémentation pour ceux qui ont besoin de la solution :
Code : C++ - Implémentation de l'opérateur += 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 | Duree& Duree::operator+=(const Duree &duree2)
{
// 1 : ajout des secondes
m_secondes += duree2.m_secondes; // Exceptionnellement autorisé car même classe
// Si le nombre de secondes dépasse 60, on rajoute des minutes et on met un nombre de secondes inférieur à 60
m_minutes += m_secondes / 60;
m_secondes %= 60;
// 2 : ajout des minutes
m_minutes += duree2.m_minutes;
// Si le nombre de minutes dépasse 60, on rajoute des heures et on met un nombre de minutes inférieur à 60
m_heures += m_minutes / 60;
m_minutes %= 60;
// 3 : ajout des heures
m_heures += duree2.m_heures;
return *this;
}
|
Ce n'est pas un algorithme ultra-complexe, mais comme je vous avais dit il faut réfléchir un tout petit peu pour pouvoir l'écrire quand même.
Comme nous sommes dans une méthode de la classe, nous pouvons directement modifier les attributs. On va y ajouter les heures, minutes et secondes de l'objet reçu en paramètre, à savoir duree2. On a ici exceptionnellement le droit d'accéder directement aux attributs de cet objet car on se trouve dans une méthode de la même classe. C'est un peu tordu mais ça nous aide bien (sinon il aurait fallu créer des méthodes "accesseur" comme getHeures()).
Rajouter les secondes, c'est facile. Mais ensuite on doit rajouter un reste si on a dépassé 60 secondes (donc rajouter des minutes). Je ne vous explique pas comment ça fonctionne dans le détail, je vous laisse vous remuer les méninges un peu, ce n'est vraiment pas bien difficile (c'est du niveau des tous premiers chapitres du cours

). Vous noterez que c'est un cas où l'opérateur modulo (%), à savoir le reste de la division, est très utile.
Bref, on fait de même avec les minutes, et quant aux heures c'est encore plus facile vu qu'il n'y a pas de reste (on peut dépasser les 24 heures donc pas de problème).
Finalement, il n'y a que la dernière ligne qui devrait vous surprendre. La méthode renvoie l'objet lui-même à l'aide de
*this.
this est un mot-clé particulier du langage dont nous reparlerons dans un prochain chapitre. C'est un pointeur vers l'objet qu'on est en train de manipuler. Cette ligne peut-être traduite en français par : "Renvoie l'objet pointé par le pointeur
this". Les raisons profonde de l'existence de cette ligne ainsi que de la référence comme type de retour sont assez compliquées. Au niveau de ce cours, prenez ça comme une recette de cuisine pour vos opérateurs.
Quelques tests
Pour mes tests, j'ai dû rajouter une méthode
afficher() à la classe Duree (elle fait un cout de la durée tout bêtement).
Voilà mon bôôô main

:
Code : C++ 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 | #include <iostream>
#include "Duree.h"
using namespace std;
int main()
{
Duree duree1(0, 10, 28), duree2(0, 15, 2);
Duree resultat;
duree1.afficher();
cout << "+" << endl;
duree2.afficher();
resultat = duree1 + duree2;
cout << "=" << endl;
resultat.afficher();
return 0;
}
|
Et le tant attendu résultat à l'écran :
Code : Console | 0h10m28s
+
0h15m2s
=
0h25m30s |
Cool, ça marche.

Bon mais ça c'était trop facile, il n'y avait pas de reste dans mon calcul. Corsons un peu les choses avec d'autres valeurs :
Code : Console | 1h45m50s
+
1h15m50s
=
3h1m40s |
Yeahhh ! Ça marche ! (et du premier coup pour moi nananère

).
J'ai bien entendu testé d'autres valeurs pour être bien sûr que ça fonctionnait, mais de toute évidence ça marche très bien et mon algo est donc bon.
Bon, on en viendrait presque à oublier l'essentiel dans tout ça. Tout ce qu'on a fait là, c'était pour pouvoir écrire cette ligne :
Code : C++ | resultat = duree1 + duree2;
|
La surcharge de l'opérateur + nous a permis de rendre notre code clair, simple et lisible, alors qu'on aurait dû utiliser une méthode en temps normal.
N'oublions pas non plus l'opérateur +=. On peut tout à fait l'utiliser directement.
Code : C++ 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 | #include <iostream>
#include "Duree.h"
using namespace std;
int main()
{
Duree duree1(0, 10, 28), duree2(0, 15, 2);
duree1.afficher();
cout << "+=" << endl;
duree2.afficher();
duree1 += duree2; //Utilisation directe de l'opérateur +=
cout << "=" << endl;
duree1.afficher();
return 0;
}
|
Ce code affiche bien sûr la même chose que notre premier test. Je vous laisse essayer d'autres valeurs pour vous convaincre que tout est correct.
Télécharger le projet
Pour ceux d'entre vous qui n'auraient pas bien suivi la procédure, ou qui sont tout simplement fainéants (

), je vous propose de télécharger le projet contenant :
- main.cpp
- Duree.cpp
- Duree.h
- Ainsi que le fichier .cbp de Code::Blocks (si vous utilisez cet IDE comme moi)
Bonus track #1
Ce qui est vraiment sympa dans tout ça, c'est que tel que notre système est fait, on peut très bien additionner plusieurs durées en même temps sans aucun problème.
Par exemple, je rajoute juste une troisième durée dans mon main et je l'additionne avec les autres :
Code : C++ 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 | int main()
{
Duree duree1(1, 45, 50), duree2(1, 15, 50), duree3 (0, 8, 20);
Duree resultat;
duree1.afficher();
cout << "+" << endl;
duree2.afficher();
cout << "+" << endl;
duree3.afficher();
resultat = duree1 + duree2 + duree3;
cout << "=" << endl;
resultat.afficher();
return 0;
}
|
Code : Console | 1h45m50s
+
1h15m50s
+
0h8m20s
=
3h10m0s |
C'est cool non vous trouvez pas ?
En fait, la ligne-clé :
Code : C++ | resultat = duree1 + duree2 + duree3;
|
... revient à écrire :
Code : C++ | resultat = operator+(operator+(duree1, duree2), duree3);
|
Le tout s'imbrique dans une logique implacable et vient se placer finalement dans l'objet
resultat.
Notez que le C++ ne vous permet pas de changer la priorité des opérateurs.
Bonus track #2
Et pour notre seconde bonus track, sachez qu'on n'est pas obligé d'additionner des
Duree avec des
Duree, du temps que ça reste logique et compatible.
Par exemple, on pourrait très bien additionner une
Duree et un
int. On considérerait dans ce cas que le nombre
int est un nombre de secondes à ajouter.
Cela nous permettra d'écrire par exemple :
Code : C++
Vive la surcharge des fonctions et des méthodes! La fonction
operator+ se défini en utilisant le même "truc" qu'avant :
Code : C++ | Duree operator+(Duree const& duree, int secondes)
{
Duree copie(duree);
copie += secondes;
return copie;
}
|
et tous les calculs sont reportés dans la méthode
operator+=, comme précédemment.
Code : C++ | Duree& operator+=(int secondes);
|
... mais vous croyiez tout de même pas que j'allais vous écrire l'implémentation. Allez hop hop hop au boulot !
Les autres opérateurs arithmétiques
Maintenant que vous avez vu assez en détail le cas d'un opérateur (celui d'addition pour ceux qui ont la mémoire courte

), vous allez voir que pour la plupart des autres opérateurs c'est très facile et qu'il n'y a pas de difficulté supplémentaire. Le tout est de s'en servir correctement pour la classe que l'on manipule.
Ces opérateurs sont du même "type" que l'addition. Vous les connaissez déjà :
- La soustraction (-)
- La multiplication (*)
- La division (/)
- Le modulo (%), c'est-à-dire le reste de la division
Pour surcharger ces opérateurs, rien de plus simple : créez une fonction dont le nom commence par operator suivi de l'opérateur en question. Cela donne donc :
- operator-()
- operator*()
- operator/()
- operator%()
Avec bien sûr les versions raccourcies correspondantes sous forme de méthodes.
- operator-=()
- operator*=()
- operator/=()
- operator%=()
Pour notre classe Duree, il peut être intéressant de définir la soustraction (
operator-).
Je vous laisse le soin de le faire, en vous basant sur l'addition ça ne devrait pas être trop compliqué.
En revanche, les autres opérateurs ne servent a priori à rien : en effet, on ne multiplie pas des durées entre elles, et on les divise encore moins. Comme quoi, tous les opérateurs ne sont pas utiles à toutes les classes : ne définissez donc que ceux qui vous seront vraiment utiles.