SlideShare ist ein Scribd-Unternehmen logo
1 von 62
Downloaden Sie, um offline zu lesen
Aziz DAROUICHI
1
Programmation Orientée Objet en C++:
Surcharge des opérateurs
2
Contexte
Exemples d’appels d’opérateurs
Surcharge (ou surdéfinition) de fonction
Surcharge interne et surcharge externe
Fonctions amies
Surcharge externe et friendship
Les opérateurs de comparaison
Lien entre opérateurs
Surcharge interne ou Surcharge externe?
Surcharges de quelques opérateurs usuels
Pourquoi const en type de retour ?
Pourquoi operator<< retourne-t-il un ostream& ?
Quel type de retour pour operator+= ?
Surcharge de l’opérateur d’affectation
Q & A
Contexte
Exemples d’appels d’opérateurs
Surcharge (ou surdéfinition) de fonction
Surcharge interne et surcharge externe
Fonctions amies
Surcharge externe et friendship
Les opérateurs de comparaison
Lien entre opérateurs
Surcharge interne ou Surcharge externe?
Surcharges de quelques opérateurs usuels
Pourquoi const en type de retour ?
Pourquoi operator<< retourne-t-il un ostream& ?
Quel type de retour pour operator+= ?
Surcharge de l’opérateur d’affectation
Q & A
Chapitre 6: Surcharge des opérateurs
2
Exemple (1/2):
class Complexe{
private:
double real;
double img;
public:
Complexe(double a, double b): real(a), img(b){}
Complexe(): Complexe(0.0, 0.0){}
//add(): méthode d’instance qui additionne 2 nombres complexes
Complexe add(Complexe const&c) {
Complexe s;
s.real = real+c.real;
s.img = real+c.img;
return s;
}
void afficher() {
cout << "Nombre complexe: " << real << " ," << img << endl;
}
};
33
Contexte
Exemple (2/2):
Output:
Nombre complexe: 4.5, 6.5
Nombre complexe: 9, 12.3
int main(){
Complexe z1(1.5, 2.5), z2(3, 4), z3(4.5, 5.8), z4, z5;
z4 = z1.add(z2);
z4.afficher();
z5 = z1.add(z2.add(z3));
z5.afficher();
}
44
Contexte
Il est quand même plus naturel d’écrire :
z4 = z1 + z2 ; //que z4 = z1.add(z2);
z5 = z1 + z2 + z3; //que z3 = z1.add(z2.add(z3));
De même, on préfèrera unifier l’affichage :
cout << "z3 = " << z3 << endl;
plutôt que d’écrire :
cout << "z3 = ";
z3.afficher();
cout << endl;
55
Contexte
a + b operator+(a, b) ou a.operator+(b)
b + a operator+(b, a) ou b.operator+(a)
-a operator-(a) ou a.operator-()
cout << a operator<<(cout, a) ou cout.operator<<(a)
a = b a.operator=(b)
a += b operator+=(a, b) ou a.operator+=(b)
++a operator++(a) ou a.operator++()
not a operator not(a) ou a.operator not()
ou operator!(a) ou a.operator!()
66
Exemples d’appels d’opérateurs
Surcharge (ou surdéfinition) de fonction
7
Rappel:
Deux fonctions ayant le même nom mais pas les mêmes paramètres.
Exemple :
int max(int, int);
double max(double, double);
De la même façon, on va pouvoir écrire plusieurs fonctions pour les
opérateurs;
Exemple :
Complexe operator+(Complexe, Complexe);
Matrice operator+(Matrice, Matrice);
7
Surcharge interne et surcharge externe
8
La surcharge des opérateurs peut être réalisée
soit à l’extérieur (par des fonctions),
Complexe operator+(Complexe, Complexe);
soit à l’intérieur (par des méthodes)
class Complexe{
public:
Complexe operator+(Complexe) const;
};
de la classe à laquelle ils s’appliquent.
8
Surcharge externe
9
Vu que les attributs sont privés et donc inaccessibles depuis l'extérieur
de la classe. Il existe trois solutions pour la surcharge externe des
opérateurs:
via des accesseurs,
via une autre méthode créée dans la classe,
via le concept d’amitié.
9
Surcharge externe
10
Exemple (1/2):
10
class Complexe{
private:
double real;
double img;
public:
Complexe(double a, double b): real(a), img(b){}
Complexe(): Complexe(0.0, 0.0){}
void afficher() const{
cout << "Nombre complexe: " << real << " ," << img << endl;}
double getReal() const { return real;}
double getImg() const { return img;}
};//Fin de la classe
const Complexe operator+(Complexe const& z1, Complexe const& z2){
Complexe z3( z1.getReal() + z2.getReal(), z1.getImg() + z2.getImg() );
return z3;
}
Exemple (2/2):
Output:
Nombre complexe: 4.5, 6.5
Nombre complexe: 9, 10.3
int main(){
Complexe z1(1.5, 2.5), z2(3, 4), z3(4.5, 5.8), z4, z5;
z4 = z1+z2;
z4.afficher();
z5 = z1+z2+z3;
z5.afficher();
}
1111
Surcharge externe
Choix du prototype
Base :
Complexe operator+(Complexe z1, Complexe z2);
Optimisation :
1. Complexe operator+(Complexe const& z1, Complexe const& z2);
2. Complexe& operator+(Complexe const& z1, Complexe const& z2);
3. const Complexe operator+(Complexe const& z1, Complexe const& z2);
C++11:
const Complexe operator+(Complexe z1, Complexe const& z2);
1212
Surcharge externe
Les opérateurs de flux
Exemple:
cout << z1; operator<<(cout, z1);
Le prototype est:
ostream& operator<<(ostream&, Complexe const&);
Le paramètre ostream est passé par référence,
On passe le deuxième paramètre par référence constante, vu que l’on ne fait qu'afficher
le contenu de ce paramètre et rien d’autre.
Le type de retour est une référence sur l’objet ostream, ce qui permet de faire une chaîne :
cout << z1 << z2 << z3;
1313
Surcharge externe
Définitions de l’opérateur d’affichage
Via des accesseurs :
ostream& operator<<(ostream& sortie, Complexe const& z){
sortie << "( " << z.getReal() << ", " << z.getImg() << ")" ;
return sortie;
}
1414
Surcharge externe
Définitions de l’opérateur d’affichage
Via des accesseurs :
1515
Surcharge externe
class Complexe{
private:
double real;
double img;
public:
…
double getReal() const { return real;}
double getImg() const { return img;}
}; //Fin de la classe
ostream& operator<<(ostream& sortie, Complexe const& z){
sortie << "( " << z.getReal() << ", " << z.getImg() << ")" ;
return sortie;
}
int main(){
Complexe z1(1.5, 2.5);
cout << z1; //affiche: (1.5, 2.5)
}
Définitions de l’opérateur d’affichage
Via une autre méthode :
ostream& operator<<(ostream& sortie, Complexe const& z){
return z.afficher(sortie);
}
avec
ostream& afficher(ostream& flux){
flux << "( " << real << ", " << img << ")" ;
return flux;
}
1616
Surcharge externe
Définitions de l’opérateur d’affichage
Via une autre méthode :
1717
Surcharge externe
class Complexe{
private:
double real; double img;
public:
…
double getReal() const { return real;}
double getImg() const { return img;}
ostream& afficher(ostream& flux){ //afficher() est une méthode d’instance
flux << "( " << real << ", " << img << ")" ;
return flux; }
}; //Fin de la classe
ostream& operator<<(ostream& sortie, Complexe const& z){
return z.afficher(sortie);
}
int main(){
Complexe z1(1.5, 2.5); cout << z1; //affiche: (1.5, 2.5)
}
Définitions de l’opérateur d’affichage
Via le concept d'amitié
1818
Surcharge externe
Une fonction membre a accès aux membres publics et privés de la
classe.
Une classe avait généralement des membres privés (principe
d’encapsulation), et que ceux-ci n’étaient pas accessibles par des
fonctions non-membres.
Dans certains cas, cependant, on souhaite pouvoir utiliser une
fonction qui puisse accéder aux membres d’une classe, sans toutefois
nécessairement disposer d’une instance de cette classe par laquelle
l’appeler.
Fonctions amies
19
Une fonction amie d’une classe est une fonction qui, sans être
membre de cette classe, a le droit d’accéder à tous ses membres.
Une fonction est l’amie (friend) d’une classe lorsqu’elle est autorisée
à adresser directement les membres privés de cette classe.
L'amitié est déclarée en utilisant le mot-clé réservé: friend
Il importe peu de déclarer une fonction amie dans la partie public ou
private d'une classe donnée. Dans les deux cas, la fonction sera vue
comme étant une fonction public.
Fonctions amies
20
Exemple:
La fonction afficher() est une fonction amie de la classe Tableau.
Déclarer la fonction afficher() private ou public importe peu.
Fonctions amies
class Tableau{
private:
int nbr; // la taille du tableau tab
double* tab; // pointeur vers un tableau de double
friend void afficher(const Tableau &);
public:
Tableau(int nbrElements);
...
};
21
void afficher(const Tableau &t){
cout << "[";
for (int i(0); i < t.nbr; i++)
cout << " " << t.tab[i];
cout << "]" ;
}
Friend
Parfois, il peut être nécessaire d’autoriser les opérateurs externes
d’accéder à certains éléments private.
Dans ce cas, ajoutez, dans la définition de la classe, leur prototype
précédé du mot clé friend :
friend const Complexe operator*(double, Complexe const&);
friend ostream& operator<<(ostream&, Complexe const&);
Le mot clé friend signifie que ces fonctions, bien que ne faisant pas
partie de la classe, peuvent avoir accès aux attributs et méthodes
private de la classe.
Les définitions restent hors de la classe (et sans le mot clé friend).
2222
Surcharge externe et friendship
Exemple (1/2):
class Complexe{
friend ostream& operator<<(ostream& sortie, Complexe const& z); //Notez la présence
//de friend
private:
double real;
double img;
public:
Complexe(double a, double b): real(a), img(b){}
Complexe(): Complexe(0.0, 0.0) {}
}; //Fin de la classe
ostream& operator<<(ostream& sortie, Complexe const& z){
sortie << "(" << z.real << ", " << z.img << ")";
return sortie;
}
2323
Surcharge externe et friendship
Exemple (2/2):
int main(){
Complexe z(1.5, 2.5);
cout << z;
return 0;
}
Output:
(1.5, 2.5)
2424
Surcharge externe et friendship
2525
Surcharge externe et friendship
L’opérateur de flux >>
On va surcharger l’opérateur >> pour la classe Complexe.
Voici le prototype de cet opérateur:
istream& operator>>(istream&, Complexe & );
Le paramètre istream est passé par référence,
Le deuxième paramètre est passé par référence, il représente le nombre
complexe à définir.
Le type de retour est une référence sur istream, ce qui permet de faire une
chaîne :
cin >> z1 >> z2;
2626
Surcharge de l’opérateur de flux >>
Exemple (1/2):
class Complexe{
friend ostream& operator<<(ostream& sortie, Complexe const& z); //surcharge de l'opérateur <<
friend istream& operator>>(istream& flux, Complexe& z); //surcharge de l'opérateur >>
private:
double real; double img;
public:
Complexe(double a, double b): real(a), img(b){}
Complexe(): Complexe(0.0, 0.0) {}
}; //Fin de la classe
ostream& operator<<(ostream& sortie, Complexe const& z){
sortie << "(" << z.real << ", " << z.img << ")";
return sortie;}
istream& operator>>(istream& flux, Complexe & z){
cout << "Partie réelle ?" << endl;
flux >> z.real;
cout << "Partie imaginaire ?" << endl;
flux >> z.img;
return flux;
}
2727
Surcharge de l’opérateur de flux >>
Exemple (2/2):
int main(){
Complexe z;
cin >> z;
cout << z;
return 0;
}
Output:
Partie réelle ?
3.5
Partie imaginaire ?
4.5
(3.5, 4.5)
Les opérateurs de comparaison
On va surcharger les opérateurs de comparaison (==, !=, <, >=,...)
Voici le prototype de ces opérateurs:
bool operatorOp(NomDeLaClasse const&obj1, NomDeLaClasse const& obj2);
2828
Surcharge externe
Operateur ==
Commençons par l'opérateur de test d'égalité ==.
Pour être capables d'utiliser le symbole « == » entre deux objets, vous devez créer
une fonction ayant précisément pour nom operator==et dotée du prototype :
bool operator==(Complexe const& a, Complexe const& b);
Mode d'utilisation
if (z1 == z2){
std::cout << "Les deux nombres complexes sont égaux !" << std::endl;
}
2929
Les opérateurs de comparaison
L'implémentation de l'opérateur==
1ère solution avec friend
Voici la définition de la fonction operator==:
bool operator== (Complexe const& a, Complexe const& b){
if (a.real == b.real && a.img == b. img)
return true;
else
return false;
}
Ou directement
bool operator== (Complexe const& a, Complexe const& b){
return (a.real == b.real && a.img == b. img);
}
3030
Les opérateurs de comparaison
L'implémentation de l'opérateur==
1ère solution avec friend
Dans la définition de la classe, vous ajoutez le prototype de l’opérateur ==:
3131
Les opérateurs de comparaison
class Complexe{
friend operator==(Complexe const& a, Complexe const& b);
private:
double real;
double img;
public:
Complexe(double, double);
Complexe();
//…
};
bool operator==(Complexe const& a, Complexe const& b) {
return (a.real == b.real && a.img == b.img);
}
L'implémentation de l'opérateur==
2ème solution avec méthode d’instance
On commence par créer une méthode publique estEgal() qui renvoie true si b est
égal à l'objet dont on a appelé la méthode:
bool Complexe::estEgal(Complexe const& b){
return (real == b.real && img == b. img);
}
Et on utilise cette méthode dans l'opérateur de comparaison :
bool operator==(Complexe const& a, Complexe const& b) {
return a.estEgal(b);
}
3232
Les opérateurs de comparaison
L'implémentation de l'opérateur==
2ème solution avec méthode d’instance
À côté de la classe, vous rajoutez la définition de l’opérateur ==:
3333
Les opérateurs de comparaison
class Complexe{
private:
double real;
double img;
public:
Complexe(double, double);
Complexe();
bool estEgal(Complexe const&);
//…
};
bool operator==(Complexe const& a, Complexe const& b) {
return a.estEgal(b);
}
L'implémentation de l'opérateur==
Dans le main(), on peut faire un simple test de comparaison pour vérifier que l'on a
fait les choses correctement :
Output:
Les nombres complexes sont identiques
3434
Les opérateurs de comparaison
int main(){
Complexe z1(1.0, 2.0), z2(1.0, 2.0);
if (z1 == z2)
cout << "Les nombres complexes sont identiques";
else
cout << "Les nombres complexes sont différents";
return 0;
}
Operateur !=
Pour tester si deux objets sont différents, il suffit de tester s'ils ne sont pas égaux!
Voici la définition de la fonction operator==:
bool operator!=(Complexe const& a, Complexe const& b) {
if (a == b) //On utilise l'opérateur == qu'on a défini précédemment !
return false; //S'ils sont égaux, alors ils ne sont pas différents
else
return true; //Et s'ils ne sont pas égaux, c'est qu'ils sont différents
}
Ou en version courte
bool operator!=(Complexe const& a, Complexe const& b) {
return !(a==b); //On utilise l'opérateur == qu'on a défini précédemment !
}
3535
Les opérateurs de comparaison
L'implémentation de l'opérateur!=
Dans la définition de la classe, vous rajoutez le prototype de l’opérateur !=:
3636
Les opérateurs de comparaison
class Complexe{
friend operator==(Complexe const& a, Complexe const& b);
friend operator!= (Complexe const& a, Complexe const& b);
private:
double real;
double img;
public:
//…
};
bool operator==(Complexe const& a, Complexe const& b) {
return (a.real == b.real && a.img == b. img);
}
bool operator!=(Complexe const& a, Complexe const& b) {
return !(a==b); //On utilise l'opérateur ==
}
L'implémentation de l'opérateur!=
Dans le main(), on peut faire un simple test de comparaison:
Output:
Les nombres complexes sont identiques
3737
Les opérateurs de comparaison
int main(){
Complexe z1(1.0, 2.0), z2(1.0, 2.0);
if (z1 != z2)
cout << "Les nombres complexes sont différents ";
else
cout << "Les nombres complexes sont identiques ";
return 0;
}
Les autres opérateurs de comparaison
Il nous reste encore quatre autres opérateurs de comparaison: <, >, <= et >=.
Pour vous aider, je vous donne les prototypes:
bool operator<(NomDeLaClasse const &a, NomDeLaClasse const& b);
bool operator>(NomDeLaClasse const &a, NomDeLaClasse const& b);
bool operator<=(NomDeLaClasse const &a, NomDeLaClasse const& b);
bool operator>=(NomDeLaClasse const &a, NomDeLaClasse const& b);
3838
Les opérateurs de comparaison
Pour surcharger un opérateur Op dans une classe NomDeLaClasse, il
faut ajouter la méthode operatorOp dans la classe en question :
class NomDeLaClasse{
...
// prototype de l’opérateur Op
type_de_retour operatorOp(types_de_parametres);
...
};
// définition de l’opérateur Op
type_de_retour NomDeLaClasse::operatorOp(types_de_parametres) {
...
}
3939
Surcharge interne
4040
Surcharge interne
Exemple (1/3):
class Complexe {
friend ostream& operator<<(ostream& sortie, Complexe const& z);
private:
double real;
double img;
public:
Complexe(double, double);
Complexe();
void operator+=(Complexe const& z2); // z1 += z2;
};
4141
Surcharge interne
Exemple (2/3):
Complexe::Complexe(double a, double b):real(a), img(b){}
Complexe::Complexe(): Complexe(0.0, 0.0) {}
void Complexe::operator+=(Complexe const& z2){
real += z2.real;
img += z2.img;
}
ostream& operator<<(ostream& sortie, Complexe const& z) {
sortie << "(" << z.real << ", " << z.img << ")";
return sortie;
}
4242
Surcharge interne
Exemple (3/3):
int main(){
Complexe z1(1.5, 2.5), z2(1.3, 1.4);
z1+=z2;
cout << z1;
return 0;
}
Output:
(2.8, 3.9)
4343
Lien entre opérateurs
L’on veut exprimer le lien sémantique des opérateurs + et += (dans les
deux cas, la même opération somme)
L’opérateur += est par nature un opérateur demandant moins de
traitement car il ne créé pas de nouvel objet Complexe.
Toujours, il vaut mieux de définir le plus lourd en fonction du plus
léger.
4444
Lien entre opérateurs
Exemple:
const Complexe operator+(Complexe z1, Complexe const& z2){
z1+=z2; // utilise l'opérateur += redéfini précédemment
return z1;
}
Le paramètre z1 étant passé par valeur, ici z1 est locale à la fonction.
4545
Lien entre opérateurs
Exemple (1/3):
class Complexe {
friend ostream& operator<<(ostream& sortie, Complexe const& z);
friend const Complexe operator+(Complexe z1, Complexe const& z2);
private:
double real;
double img;
public:
Complexe(double, double);
Complexe();
void operator+=(Complexe const& z2); // z1 += z2;
};
4646
Lien entre opérateurs
Exemple (2/3):
Complexe::Complexe(double a, double b):real(a), img(b){}
Complexe::Complexe(): Complexe(0.0, 0.0) {}
void Complexe::operator+=(Complexe const& z2){
real += z2.real;
img += z2.img;
}
const Complexe operator+(Complexe z1, Complexe const& z2){
z1 += z2; // utilise l'opérateur += redéfini précédemment
return z1;
}
ostream& operator<<(ostream& sortie, Complexe const& z) {
sortie << "(" << z.real << ", " << z.img << ")";
return sortie;
}
4747
Lien entre opérateurs
Exemple (3/3):
int main(){
Complexe z1(1.5, 2.5), z2(1.3, 1.4), z3;
z3=z1+z2;
cout << z3;
return 0;
}
Output:
(2.8, 3.9)
4848
Surcharge interne ou Surcharge externe?
Les opérateurs propres à une classe peuvent être surchargés en interne
ou en externe :
z3 = z1 + z2; SOIT z3 = z1.operator+(z2);
SOIT z3 = operator+(z1, z2);
4949
Surcharge interne ou Surcharge externe?
Surcharge interne:
class Complexe {
public:
const Complexe operator+(Complexe const& z2) const;
// ....
};
const Complexe Complexe::operator+(Complexe const& z2) const{…}
Surcharge externe:
const Complexe operator+(Complexe z1, Complexe const& z2){…};
// ...
class Complexe {
friend const Complexe operator+(Complexe z1, Complexe const& z2);
// ...
};
5050
Préférez la surcharge externe chaque fois que vous pouvez le faire
SANS friend
c.-à-d. chaque fois que vous pouvez écrire l’opérateur à l’aide de
l’interface de la classe (et sans copies inutiles).
Préférez la surcharge interne si l’opérateur est « proche de la classe »,
c.-à-d. nécessite des accès internes ou des copies supplémentaires
inutiles (typiquement operator+=).
Surcharge interne ou Surcharge externe?
5151
Exemples:
bool operator==(NomDeLaClasse const&) const; // ex: p == q
bool operator<(NomDeLaClasse const&) const; // ex: p < q
NomDeLaClasse& operator+=(NomDeLaClasse const&); // ex: p += q
NomDeLaClasse& operator-=(NomDeLaClasse const&); // ex: p -= q
NomDeLaClasse& operator*=(autre_type const); // ex: p *= x;
NomDeLaClasse& operator++(); // ex: ++p
NomDeLaClasse& operator++(int inutile); // ex: p++
const NomDeLaClasse operator-() const; // ex: r = -p;
//Surcharges externes
const NomDeLaClasse operator+(NomDeLaClasse, NomDeLaClasse const&); // r = p + q
const NomDeLaClasse operator-(NomDeLaClasse, NomDeLaClasse const&); // r = p - q
ostream& operator<<(ostream&, NomDeLaClasse const&); // ex: cout << p;
const NomDeLaClasse operator*(autre_type, NomDeLaClasse const&); // ex: q = x * p;
Surcharges de quelques opérateurs usuels
5252
const Complexe operator+(Complexe, Complexe const&);
z3 = z1 + z2;
++(z1 + z2); // pas de sens
z1 + z2 = f(x); // idem
Pourquoi const en type de retour ?
5353
ostream& operator<<(ostream& sortie, Complexe const& z){
cout << z1 << endl;
operator<<(cout << z1, endl);
operator<<(operator<<(cout, z1), endl);
Pourquoi operator<< retourne-t-il un ostream& ?
5454
z1 += z2;
void Complexe::operator+=(Complexe const&);
En C++, chaque expression fait quelque chose et vaut quelque chose :
x = Expression; z3 = (z1 += z2);
class Complexe {
// ...
Complexe& operator+=(Complexe const& z2);
// ...
};
Complexe& Complexe::operator+=(Complexe const& z2){
real += z2.real;
img += z2.real;
return *this;
}
this étant un pointeur sur un objet,*this est l'objet lui-même ! Notre opérateur
renvoie donc l'objet lui-même.
Quel type de retour pour operator+= ?
5555
Attention de ne pas utiliser la surcharge des opérateurs à mauvais
escient et à veiller à les écrire avec un soin particulier.
Les performances du programme peuvent en être gravement affectées
par des opérateurs surchargés mal écrits.
En effet, l’utilisation inconsidérée des opérateurs peut conduire à un
grand nombre de copies d’objets :
Utiliser des références dès que cela est approprié !
Avertissement
5656
Exemple:
Comparez le code suivant qui fait de 1 à 3 copies inutiles :
Complexe Complexe::operator+=(Complexe z2){
Complexe z3;
real += z2.real;
img += z2.img;
z3 = *this;
return z3;
}
Avec le code suivant qui n’en fait pas :
Complexe& Complexe::operator+=(Complexe const& z2){
real += z2.real;
img += z2.img;
return *this;
}
Avertissement
5757
L’opérateur d’affectation = (utilisé par exemple dans a = b) :
est le seul opérateur universel (il est fourni de toutes façons par défaut
pour toute classe)
est très lié au constructeur de copie, sauf que le premier s’appelle
lors d’une affectation et le second lors d’une initialisation
la version par défaut, qui fait une copie de surface, est suffisante dans
la très grande majorité des cas
si nécessaire, on peut supprimer l’opérateur d’affectation :
class ClasseNonAffectation {
// ...
private:
ClasseNonAffectation& operator=(ClasseNonAffectation const&) = delete;
};
Opérateur d’affectation
5858
Remarque:
Ne confondez pas le constructeur de copie avec la surcharge de
l'opérateur d’affectation = (operator=). Ils se ressemblent beaucoup
mais il y a une différence : le constructeur de copie est appelé lors
de l'initialisation (à la création de l'objet) tandis que la
méthode operator= est appelée si on essaie d'affecter un autre objet
par la suite, après son initialisation.
Exemple:
Complexe z1 = z2; // constructeur de copie
z1 = z2; // opérateur d’affectation: operator=
Opérateur d’affectation
5959
L’opérateur d’affectation (méthode operator=) effectue le même
travail que le constructeur de copie. Si l’on doit redéfinir
l’opérateur d’affectation, son implémentation est donc relativement
simple:
NomDeLaClasse& NomDeLaClasse::operator=(NomDeLaClasse const& source)
{
if (this != &source)
//On vérifie que l'objet n'est pas le même que celui reçu en argument
{
//On copie tous les champs
…
}
return *this; //On renvoie l'objet lui-même
}
Surcharge de l’opérateur d’affectation
6060
Depuis C++11, pour implémenter l’opérateur d’affectation =, on
choisit le schéma suivant :
on commencera par définir une fonction swap() pour échanger 2
objets de la classe, (sûrement en utilisant celle de la bibliothèque
utility (#include <utility>) sur les attributs)
puis on définira l’opérateur d’affectation comme suit :
NomDeLaClasse& NomDeLaClasse::operator=(NomDeLaClasse source)
// Notez le passage par VALEUR
{
swap(*this, source);
return *this;
}
Surcharge de l’opérateur d’affectation
61
Q & A
61
Références
62
1) http://www.cplusplus.com
2) https://isocpp.org/
3) https://openclassrooms.com/fr/courses/1894236-programmez-avec-le-langage-c
4) https://www.tutorialspoint.com/cplusplus/
5) https://en.cppreference.com
6) https://stackoverflow.com/
7) https://cpp.developpez.com/
8) Programmer en C++, Claude Delannoy, éditions Eyrolles, 2014.
9) Initiation à la programmation (en C++), Jean-Cédric Chappelier, & Jamila Sam,
coursera, 2018.
10) Introduction à la programmation orientée objet (en C++), Jamila Sam & Jean-
Cédric Chappelier, coursera, 2018.
62

Weitere ähnliche Inhalte

Was ist angesagt?

Was ist angesagt? (20)

programmation orienté objet c++
programmation orienté objet c++programmation orienté objet c++
programmation orienté objet c++
 
Cours c++
Cours c++Cours c++
Cours c++
 
POO Java Chapitre 1 Classe & Objet
POO Java Chapitre 1 Classe & ObjetPOO Java Chapitre 1 Classe & Objet
POO Java Chapitre 1 Classe & Objet
 
POO Java Introduction
POO Java IntroductionPOO Java Introduction
POO Java Introduction
 
POO Java Chapitre 4 Heritage et Polymorphisme
POO Java Chapitre 4 Heritage et PolymorphismePOO Java Chapitre 4 Heritage et Polymorphisme
POO Java Chapitre 4 Heritage et Polymorphisme
 
Cours Programmation Orientée Objet en C++
Cours Programmation Orientée Objet en C++Cours Programmation Orientée Objet en C++
Cours Programmation Orientée Objet en C++
 
La programmation modulaire en Python
La programmation modulaire en PythonLa programmation modulaire en Python
La programmation modulaire en Python
 
Algorithmique
AlgorithmiqueAlgorithmique
Algorithmique
 
Chapitre 4 heritage et polymorphisme
Chapitre 4 heritage et polymorphismeChapitre 4 heritage et polymorphisme
Chapitre 4 heritage et polymorphisme
 
Chap 6 : classes et interfaces
Chap 6 : classes et interfacesChap 6 : classes et interfaces
Chap 6 : classes et interfaces
 
Programmation orientée objet : Object, classe et encapsulation
Programmation orientée objet : Object, classe et encapsulationProgrammation orientée objet : Object, classe et encapsulation
Programmation orientée objet : Object, classe et encapsulation
 
Python avancé : Interface graphique et programmation évènementielle
Python avancé : Interface graphique et programmation évènementiellePython avancé : Interface graphique et programmation évènementielle
Python avancé : Interface graphique et programmation évènementielle
 
POO Java Chapitre 6 Exceptions
POO Java  Chapitre 6 ExceptionsPOO Java  Chapitre 6 Exceptions
POO Java Chapitre 6 Exceptions
 
Chapitre1: Langage Python
Chapitre1: Langage PythonChapitre1: Langage Python
Chapitre1: Langage Python
 
resume algo 2023.pdf
resume algo 2023.pdfresume algo 2023.pdf
resume algo 2023.pdf
 
Exercice 1 java Héritage
Exercice 1 java HéritageExercice 1 java Héritage
Exercice 1 java Héritage
 
COURS_PYTHON_22.ppt
COURS_PYTHON_22.pptCOURS_PYTHON_22.ppt
COURS_PYTHON_22.ppt
 
Cours java
Cours javaCours java
Cours java
 
Chapitre 2 classe et objet
Chapitre 2   classe et objetChapitre 2   classe et objet
Chapitre 2 classe et objet
 
Chap2fonctionscpp
Chap2fonctionscppChap2fonctionscpp
Chap2fonctionscpp
 

Ähnlich wie Chapitre6: Surcharge des opérateurs

Java uik-chap6-poo heritage v2 java
Java uik-chap6-poo heritage v2 javaJava uik-chap6-poo heritage v2 java
Java uik-chap6-poo heritage v2 java
Amel Morchdi
 
20140123 java8 lambdas_jose-paumard-soat
20140123 java8 lambdas_jose-paumard-soat20140123 java8 lambdas_jose-paumard-soat
20140123 java8 lambdas_jose-paumard-soat
SOAT
 
Qualité logicielle
Qualité logicielleQualité logicielle
Qualité logicielle
cyrilgandon
 

Ähnlich wie Chapitre6: Surcharge des opérateurs (20)

Ch07
Ch07Ch07
Ch07
 
Ch06
Ch06Ch06
Ch06
 
Les fonctions lambdas en C++11 et C++14
Les fonctions lambdas en C++11 et C++14Les fonctions lambdas en C++11 et C++14
Les fonctions lambdas en C++11 et C++14
 
Cours de C++, en français, 2002 - Cours 2.1
Cours de C++, en français, 2002 - Cours 2.1Cours de C++, en français, 2002 - Cours 2.1
Cours de C++, en français, 2002 - Cours 2.1
 
Scala : programmation fonctionnelle
Scala : programmation fonctionnelleScala : programmation fonctionnelle
Scala : programmation fonctionnelle
 
POO-chapitre3.pptx
POO-chapitre3.pptxPOO-chapitre3.pptx
POO-chapitre3.pptx
 
Fonctions_Inline_Amies en C++
Fonctions_Inline_Amies en C++Fonctions_Inline_Amies en C++
Fonctions_Inline_Amies en C++
 
Interception de signal avec dump de la pile d'appel
Interception de signal avec dump de la pile d'appelInterception de signal avec dump de la pile d'appel
Interception de signal avec dump de la pile d'appel
 
Ch03
Ch03Ch03
Ch03
 
JAVA-UIK-CHAP6-POO HERITAGE JAVA
JAVA-UIK-CHAP6-POO HERITAGE JAVAJAVA-UIK-CHAP6-POO HERITAGE JAVA
JAVA-UIK-CHAP6-POO HERITAGE JAVA
 
Java uik-chap6-poo heritage v2 java
Java uik-chap6-poo heritage v2 javaJava uik-chap6-poo heritage v2 java
Java uik-chap6-poo heritage v2 java
 
System c eniso_jan_fev_07
System c eniso_jan_fev_07System c eniso_jan_fev_07
System c eniso_jan_fev_07
 
Javascript ne se limite pas à jquery
Javascript ne se limite pas à jqueryJavascript ne se limite pas à jquery
Javascript ne se limite pas à jquery
 
Marzouk une introduction à jdbc
Marzouk une introduction à jdbcMarzouk une introduction à jdbc
Marzouk une introduction à jdbc
 
POO
POOPOO
POO
 
20140123 java8 lambdas_jose-paumard-soat
20140123 java8 lambdas_jose-paumard-soat20140123 java8 lambdas_jose-paumard-soat
20140123 java8 lambdas_jose-paumard-soat
 
Chapitre 04 : les fonctions
Chapitre 04 : les fonctionsChapitre 04 : les fonctions
Chapitre 04 : les fonctions
 
Qualité logicielle
Qualité logicielleQualité logicielle
Qualité logicielle
 
Chapitre 11: Expression Lambda et Référence de méthode en Java
Chapitre 11: Expression Lambda et Référence de méthode en JavaChapitre 11: Expression Lambda et Référence de méthode en Java
Chapitre 11: Expression Lambda et Référence de méthode en Java
 
Part1
Part1Part1
Part1
 

Mehr von Aziz Darouichi

Mehr von Aziz Darouichi (7)

Chapitre 2: String en Java
Chapitre 2:  String en JavaChapitre 2:  String en Java
Chapitre 2: String en Java
 
Chapitre8: Collections et Enumerations En Java
Chapitre8: Collections et Enumerations En JavaChapitre8: Collections et Enumerations En Java
Chapitre8: Collections et Enumerations En Java
 
Partie3BI-DW-OLAP2019
Partie3BI-DW-OLAP2019Partie3BI-DW-OLAP2019
Partie3BI-DW-OLAP2019
 
Partie2BI-DW2019
Partie2BI-DW2019Partie2BI-DW2019
Partie2BI-DW2019
 
Partie1BI-DW2019
Partie1BI-DW2019Partie1BI-DW2019
Partie1BI-DW2019
 
Cours Visual Basic.NET
Cours Visual Basic.NETCours Visual Basic.NET
Cours Visual Basic.NET
 
Chapitre3 tableauxcpp
Chapitre3 tableauxcppChapitre3 tableauxcpp
Chapitre3 tableauxcpp
 

Chapitre6: Surcharge des opérateurs

  • 1. Aziz DAROUICHI 1 Programmation Orientée Objet en C++: Surcharge des opérateurs
  • 2. 2 Contexte Exemples d’appels d’opérateurs Surcharge (ou surdéfinition) de fonction Surcharge interne et surcharge externe Fonctions amies Surcharge externe et friendship Les opérateurs de comparaison Lien entre opérateurs Surcharge interne ou Surcharge externe? Surcharges de quelques opérateurs usuels Pourquoi const en type de retour ? Pourquoi operator<< retourne-t-il un ostream& ? Quel type de retour pour operator+= ? Surcharge de l’opérateur d’affectation Q & A Contexte Exemples d’appels d’opérateurs Surcharge (ou surdéfinition) de fonction Surcharge interne et surcharge externe Fonctions amies Surcharge externe et friendship Les opérateurs de comparaison Lien entre opérateurs Surcharge interne ou Surcharge externe? Surcharges de quelques opérateurs usuels Pourquoi const en type de retour ? Pourquoi operator<< retourne-t-il un ostream& ? Quel type de retour pour operator+= ? Surcharge de l’opérateur d’affectation Q & A Chapitre 6: Surcharge des opérateurs 2
  • 3. Exemple (1/2): class Complexe{ private: double real; double img; public: Complexe(double a, double b): real(a), img(b){} Complexe(): Complexe(0.0, 0.0){} //add(): méthode d’instance qui additionne 2 nombres complexes Complexe add(Complexe const&c) { Complexe s; s.real = real+c.real; s.img = real+c.img; return s; } void afficher() { cout << "Nombre complexe: " << real << " ," << img << endl; } }; 33 Contexte
  • 4. Exemple (2/2): Output: Nombre complexe: 4.5, 6.5 Nombre complexe: 9, 12.3 int main(){ Complexe z1(1.5, 2.5), z2(3, 4), z3(4.5, 5.8), z4, z5; z4 = z1.add(z2); z4.afficher(); z5 = z1.add(z2.add(z3)); z5.afficher(); } 44 Contexte
  • 5. Il est quand même plus naturel d’écrire : z4 = z1 + z2 ; //que z4 = z1.add(z2); z5 = z1 + z2 + z3; //que z3 = z1.add(z2.add(z3)); De même, on préfèrera unifier l’affichage : cout << "z3 = " << z3 << endl; plutôt que d’écrire : cout << "z3 = "; z3.afficher(); cout << endl; 55 Contexte
  • 6. a + b operator+(a, b) ou a.operator+(b) b + a operator+(b, a) ou b.operator+(a) -a operator-(a) ou a.operator-() cout << a operator<<(cout, a) ou cout.operator<<(a) a = b a.operator=(b) a += b operator+=(a, b) ou a.operator+=(b) ++a operator++(a) ou a.operator++() not a operator not(a) ou a.operator not() ou operator!(a) ou a.operator!() 66 Exemples d’appels d’opérateurs
  • 7. Surcharge (ou surdéfinition) de fonction 7 Rappel: Deux fonctions ayant le même nom mais pas les mêmes paramètres. Exemple : int max(int, int); double max(double, double); De la même façon, on va pouvoir écrire plusieurs fonctions pour les opérateurs; Exemple : Complexe operator+(Complexe, Complexe); Matrice operator+(Matrice, Matrice); 7
  • 8. Surcharge interne et surcharge externe 8 La surcharge des opérateurs peut être réalisée soit à l’extérieur (par des fonctions), Complexe operator+(Complexe, Complexe); soit à l’intérieur (par des méthodes) class Complexe{ public: Complexe operator+(Complexe) const; }; de la classe à laquelle ils s’appliquent. 8
  • 9. Surcharge externe 9 Vu que les attributs sont privés et donc inaccessibles depuis l'extérieur de la classe. Il existe trois solutions pour la surcharge externe des opérateurs: via des accesseurs, via une autre méthode créée dans la classe, via le concept d’amitié. 9
  • 10. Surcharge externe 10 Exemple (1/2): 10 class Complexe{ private: double real; double img; public: Complexe(double a, double b): real(a), img(b){} Complexe(): Complexe(0.0, 0.0){} void afficher() const{ cout << "Nombre complexe: " << real << " ," << img << endl;} double getReal() const { return real;} double getImg() const { return img;} };//Fin de la classe const Complexe operator+(Complexe const& z1, Complexe const& z2){ Complexe z3( z1.getReal() + z2.getReal(), z1.getImg() + z2.getImg() ); return z3; }
  • 11. Exemple (2/2): Output: Nombre complexe: 4.5, 6.5 Nombre complexe: 9, 10.3 int main(){ Complexe z1(1.5, 2.5), z2(3, 4), z3(4.5, 5.8), z4, z5; z4 = z1+z2; z4.afficher(); z5 = z1+z2+z3; z5.afficher(); } 1111 Surcharge externe
  • 12. Choix du prototype Base : Complexe operator+(Complexe z1, Complexe z2); Optimisation : 1. Complexe operator+(Complexe const& z1, Complexe const& z2); 2. Complexe& operator+(Complexe const& z1, Complexe const& z2); 3. const Complexe operator+(Complexe const& z1, Complexe const& z2); C++11: const Complexe operator+(Complexe z1, Complexe const& z2); 1212 Surcharge externe
  • 13. Les opérateurs de flux Exemple: cout << z1; operator<<(cout, z1); Le prototype est: ostream& operator<<(ostream&, Complexe const&); Le paramètre ostream est passé par référence, On passe le deuxième paramètre par référence constante, vu que l’on ne fait qu'afficher le contenu de ce paramètre et rien d’autre. Le type de retour est une référence sur l’objet ostream, ce qui permet de faire une chaîne : cout << z1 << z2 << z3; 1313 Surcharge externe
  • 14. Définitions de l’opérateur d’affichage Via des accesseurs : ostream& operator<<(ostream& sortie, Complexe const& z){ sortie << "( " << z.getReal() << ", " << z.getImg() << ")" ; return sortie; } 1414 Surcharge externe
  • 15. Définitions de l’opérateur d’affichage Via des accesseurs : 1515 Surcharge externe class Complexe{ private: double real; double img; public: … double getReal() const { return real;} double getImg() const { return img;} }; //Fin de la classe ostream& operator<<(ostream& sortie, Complexe const& z){ sortie << "( " << z.getReal() << ", " << z.getImg() << ")" ; return sortie; } int main(){ Complexe z1(1.5, 2.5); cout << z1; //affiche: (1.5, 2.5) }
  • 16. Définitions de l’opérateur d’affichage Via une autre méthode : ostream& operator<<(ostream& sortie, Complexe const& z){ return z.afficher(sortie); } avec ostream& afficher(ostream& flux){ flux << "( " << real << ", " << img << ")" ; return flux; } 1616 Surcharge externe
  • 17. Définitions de l’opérateur d’affichage Via une autre méthode : 1717 Surcharge externe class Complexe{ private: double real; double img; public: … double getReal() const { return real;} double getImg() const { return img;} ostream& afficher(ostream& flux){ //afficher() est une méthode d’instance flux << "( " << real << ", " << img << ")" ; return flux; } }; //Fin de la classe ostream& operator<<(ostream& sortie, Complexe const& z){ return z.afficher(sortie); } int main(){ Complexe z1(1.5, 2.5); cout << z1; //affiche: (1.5, 2.5) }
  • 18. Définitions de l’opérateur d’affichage Via le concept d'amitié 1818 Surcharge externe
  • 19. Une fonction membre a accès aux membres publics et privés de la classe. Une classe avait généralement des membres privés (principe d’encapsulation), et que ceux-ci n’étaient pas accessibles par des fonctions non-membres. Dans certains cas, cependant, on souhaite pouvoir utiliser une fonction qui puisse accéder aux membres d’une classe, sans toutefois nécessairement disposer d’une instance de cette classe par laquelle l’appeler. Fonctions amies 19
  • 20. Une fonction amie d’une classe est une fonction qui, sans être membre de cette classe, a le droit d’accéder à tous ses membres. Une fonction est l’amie (friend) d’une classe lorsqu’elle est autorisée à adresser directement les membres privés de cette classe. L'amitié est déclarée en utilisant le mot-clé réservé: friend Il importe peu de déclarer une fonction amie dans la partie public ou private d'une classe donnée. Dans les deux cas, la fonction sera vue comme étant une fonction public. Fonctions amies 20
  • 21. Exemple: La fonction afficher() est une fonction amie de la classe Tableau. Déclarer la fonction afficher() private ou public importe peu. Fonctions amies class Tableau{ private: int nbr; // la taille du tableau tab double* tab; // pointeur vers un tableau de double friend void afficher(const Tableau &); public: Tableau(int nbrElements); ... }; 21 void afficher(const Tableau &t){ cout << "["; for (int i(0); i < t.nbr; i++) cout << " " << t.tab[i]; cout << "]" ; }
  • 22. Friend Parfois, il peut être nécessaire d’autoriser les opérateurs externes d’accéder à certains éléments private. Dans ce cas, ajoutez, dans la définition de la classe, leur prototype précédé du mot clé friend : friend const Complexe operator*(double, Complexe const&); friend ostream& operator<<(ostream&, Complexe const&); Le mot clé friend signifie que ces fonctions, bien que ne faisant pas partie de la classe, peuvent avoir accès aux attributs et méthodes private de la classe. Les définitions restent hors de la classe (et sans le mot clé friend). 2222 Surcharge externe et friendship
  • 23. Exemple (1/2): class Complexe{ friend ostream& operator<<(ostream& sortie, Complexe const& z); //Notez la présence //de friend private: double real; double img; public: Complexe(double a, double b): real(a), img(b){} Complexe(): Complexe(0.0, 0.0) {} }; //Fin de la classe ostream& operator<<(ostream& sortie, Complexe const& z){ sortie << "(" << z.real << ", " << z.img << ")"; return sortie; } 2323 Surcharge externe et friendship
  • 24. Exemple (2/2): int main(){ Complexe z(1.5, 2.5); cout << z; return 0; } Output: (1.5, 2.5) 2424 Surcharge externe et friendship
  • 25. 2525 Surcharge externe et friendship L’opérateur de flux >> On va surcharger l’opérateur >> pour la classe Complexe. Voici le prototype de cet opérateur: istream& operator>>(istream&, Complexe & ); Le paramètre istream est passé par référence, Le deuxième paramètre est passé par référence, il représente le nombre complexe à définir. Le type de retour est une référence sur istream, ce qui permet de faire une chaîne : cin >> z1 >> z2;
  • 26. 2626 Surcharge de l’opérateur de flux >> Exemple (1/2): class Complexe{ friend ostream& operator<<(ostream& sortie, Complexe const& z); //surcharge de l'opérateur << friend istream& operator>>(istream& flux, Complexe& z); //surcharge de l'opérateur >> private: double real; double img; public: Complexe(double a, double b): real(a), img(b){} Complexe(): Complexe(0.0, 0.0) {} }; //Fin de la classe ostream& operator<<(ostream& sortie, Complexe const& z){ sortie << "(" << z.real << ", " << z.img << ")"; return sortie;} istream& operator>>(istream& flux, Complexe & z){ cout << "Partie réelle ?" << endl; flux >> z.real; cout << "Partie imaginaire ?" << endl; flux >> z.img; return flux; }
  • 27. 2727 Surcharge de l’opérateur de flux >> Exemple (2/2): int main(){ Complexe z; cin >> z; cout << z; return 0; } Output: Partie réelle ? 3.5 Partie imaginaire ? 4.5 (3.5, 4.5)
  • 28. Les opérateurs de comparaison On va surcharger les opérateurs de comparaison (==, !=, <, >=,...) Voici le prototype de ces opérateurs: bool operatorOp(NomDeLaClasse const&obj1, NomDeLaClasse const& obj2); 2828 Surcharge externe
  • 29. Operateur == Commençons par l'opérateur de test d'égalité ==. Pour être capables d'utiliser le symbole « == » entre deux objets, vous devez créer une fonction ayant précisément pour nom operator==et dotée du prototype : bool operator==(Complexe const& a, Complexe const& b); Mode d'utilisation if (z1 == z2){ std::cout << "Les deux nombres complexes sont égaux !" << std::endl; } 2929 Les opérateurs de comparaison
  • 30. L'implémentation de l'opérateur== 1ère solution avec friend Voici la définition de la fonction operator==: bool operator== (Complexe const& a, Complexe const& b){ if (a.real == b.real && a.img == b. img) return true; else return false; } Ou directement bool operator== (Complexe const& a, Complexe const& b){ return (a.real == b.real && a.img == b. img); } 3030 Les opérateurs de comparaison
  • 31. L'implémentation de l'opérateur== 1ère solution avec friend Dans la définition de la classe, vous ajoutez le prototype de l’opérateur ==: 3131 Les opérateurs de comparaison class Complexe{ friend operator==(Complexe const& a, Complexe const& b); private: double real; double img; public: Complexe(double, double); Complexe(); //… }; bool operator==(Complexe const& a, Complexe const& b) { return (a.real == b.real && a.img == b.img); }
  • 32. L'implémentation de l'opérateur== 2ème solution avec méthode d’instance On commence par créer une méthode publique estEgal() qui renvoie true si b est égal à l'objet dont on a appelé la méthode: bool Complexe::estEgal(Complexe const& b){ return (real == b.real && img == b. img); } Et on utilise cette méthode dans l'opérateur de comparaison : bool operator==(Complexe const& a, Complexe const& b) { return a.estEgal(b); } 3232 Les opérateurs de comparaison
  • 33. L'implémentation de l'opérateur== 2ème solution avec méthode d’instance À côté de la classe, vous rajoutez la définition de l’opérateur ==: 3333 Les opérateurs de comparaison class Complexe{ private: double real; double img; public: Complexe(double, double); Complexe(); bool estEgal(Complexe const&); //… }; bool operator==(Complexe const& a, Complexe const& b) { return a.estEgal(b); }
  • 34. L'implémentation de l'opérateur== Dans le main(), on peut faire un simple test de comparaison pour vérifier que l'on a fait les choses correctement : Output: Les nombres complexes sont identiques 3434 Les opérateurs de comparaison int main(){ Complexe z1(1.0, 2.0), z2(1.0, 2.0); if (z1 == z2) cout << "Les nombres complexes sont identiques"; else cout << "Les nombres complexes sont différents"; return 0; }
  • 35. Operateur != Pour tester si deux objets sont différents, il suffit de tester s'ils ne sont pas égaux! Voici la définition de la fonction operator==: bool operator!=(Complexe const& a, Complexe const& b) { if (a == b) //On utilise l'opérateur == qu'on a défini précédemment ! return false; //S'ils sont égaux, alors ils ne sont pas différents else return true; //Et s'ils ne sont pas égaux, c'est qu'ils sont différents } Ou en version courte bool operator!=(Complexe const& a, Complexe const& b) { return !(a==b); //On utilise l'opérateur == qu'on a défini précédemment ! } 3535 Les opérateurs de comparaison
  • 36. L'implémentation de l'opérateur!= Dans la définition de la classe, vous rajoutez le prototype de l’opérateur !=: 3636 Les opérateurs de comparaison class Complexe{ friend operator==(Complexe const& a, Complexe const& b); friend operator!= (Complexe const& a, Complexe const& b); private: double real; double img; public: //… }; bool operator==(Complexe const& a, Complexe const& b) { return (a.real == b.real && a.img == b. img); } bool operator!=(Complexe const& a, Complexe const& b) { return !(a==b); //On utilise l'opérateur == }
  • 37. L'implémentation de l'opérateur!= Dans le main(), on peut faire un simple test de comparaison: Output: Les nombres complexes sont identiques 3737 Les opérateurs de comparaison int main(){ Complexe z1(1.0, 2.0), z2(1.0, 2.0); if (z1 != z2) cout << "Les nombres complexes sont différents "; else cout << "Les nombres complexes sont identiques "; return 0; }
  • 38. Les autres opérateurs de comparaison Il nous reste encore quatre autres opérateurs de comparaison: <, >, <= et >=. Pour vous aider, je vous donne les prototypes: bool operator<(NomDeLaClasse const &a, NomDeLaClasse const& b); bool operator>(NomDeLaClasse const &a, NomDeLaClasse const& b); bool operator<=(NomDeLaClasse const &a, NomDeLaClasse const& b); bool operator>=(NomDeLaClasse const &a, NomDeLaClasse const& b); 3838 Les opérateurs de comparaison
  • 39. Pour surcharger un opérateur Op dans une classe NomDeLaClasse, il faut ajouter la méthode operatorOp dans la classe en question : class NomDeLaClasse{ ... // prototype de l’opérateur Op type_de_retour operatorOp(types_de_parametres); ... }; // définition de l’opérateur Op type_de_retour NomDeLaClasse::operatorOp(types_de_parametres) { ... } 3939 Surcharge interne
  • 40. 4040 Surcharge interne Exemple (1/3): class Complexe { friend ostream& operator<<(ostream& sortie, Complexe const& z); private: double real; double img; public: Complexe(double, double); Complexe(); void operator+=(Complexe const& z2); // z1 += z2; };
  • 41. 4141 Surcharge interne Exemple (2/3): Complexe::Complexe(double a, double b):real(a), img(b){} Complexe::Complexe(): Complexe(0.0, 0.0) {} void Complexe::operator+=(Complexe const& z2){ real += z2.real; img += z2.img; } ostream& operator<<(ostream& sortie, Complexe const& z) { sortie << "(" << z.real << ", " << z.img << ")"; return sortie; }
  • 42. 4242 Surcharge interne Exemple (3/3): int main(){ Complexe z1(1.5, 2.5), z2(1.3, 1.4); z1+=z2; cout << z1; return 0; } Output: (2.8, 3.9)
  • 43. 4343 Lien entre opérateurs L’on veut exprimer le lien sémantique des opérateurs + et += (dans les deux cas, la même opération somme) L’opérateur += est par nature un opérateur demandant moins de traitement car il ne créé pas de nouvel objet Complexe. Toujours, il vaut mieux de définir le plus lourd en fonction du plus léger.
  • 44. 4444 Lien entre opérateurs Exemple: const Complexe operator+(Complexe z1, Complexe const& z2){ z1+=z2; // utilise l'opérateur += redéfini précédemment return z1; } Le paramètre z1 étant passé par valeur, ici z1 est locale à la fonction.
  • 45. 4545 Lien entre opérateurs Exemple (1/3): class Complexe { friend ostream& operator<<(ostream& sortie, Complexe const& z); friend const Complexe operator+(Complexe z1, Complexe const& z2); private: double real; double img; public: Complexe(double, double); Complexe(); void operator+=(Complexe const& z2); // z1 += z2; };
  • 46. 4646 Lien entre opérateurs Exemple (2/3): Complexe::Complexe(double a, double b):real(a), img(b){} Complexe::Complexe(): Complexe(0.0, 0.0) {} void Complexe::operator+=(Complexe const& z2){ real += z2.real; img += z2.img; } const Complexe operator+(Complexe z1, Complexe const& z2){ z1 += z2; // utilise l'opérateur += redéfini précédemment return z1; } ostream& operator<<(ostream& sortie, Complexe const& z) { sortie << "(" << z.real << ", " << z.img << ")"; return sortie; }
  • 47. 4747 Lien entre opérateurs Exemple (3/3): int main(){ Complexe z1(1.5, 2.5), z2(1.3, 1.4), z3; z3=z1+z2; cout << z3; return 0; } Output: (2.8, 3.9)
  • 48. 4848 Surcharge interne ou Surcharge externe? Les opérateurs propres à une classe peuvent être surchargés en interne ou en externe : z3 = z1 + z2; SOIT z3 = z1.operator+(z2); SOIT z3 = operator+(z1, z2);
  • 49. 4949 Surcharge interne ou Surcharge externe? Surcharge interne: class Complexe { public: const Complexe operator+(Complexe const& z2) const; // .... }; const Complexe Complexe::operator+(Complexe const& z2) const{…} Surcharge externe: const Complexe operator+(Complexe z1, Complexe const& z2){…}; // ... class Complexe { friend const Complexe operator+(Complexe z1, Complexe const& z2); // ... };
  • 50. 5050 Préférez la surcharge externe chaque fois que vous pouvez le faire SANS friend c.-à-d. chaque fois que vous pouvez écrire l’opérateur à l’aide de l’interface de la classe (et sans copies inutiles). Préférez la surcharge interne si l’opérateur est « proche de la classe », c.-à-d. nécessite des accès internes ou des copies supplémentaires inutiles (typiquement operator+=). Surcharge interne ou Surcharge externe?
  • 51. 5151 Exemples: bool operator==(NomDeLaClasse const&) const; // ex: p == q bool operator<(NomDeLaClasse const&) const; // ex: p < q NomDeLaClasse& operator+=(NomDeLaClasse const&); // ex: p += q NomDeLaClasse& operator-=(NomDeLaClasse const&); // ex: p -= q NomDeLaClasse& operator*=(autre_type const); // ex: p *= x; NomDeLaClasse& operator++(); // ex: ++p NomDeLaClasse& operator++(int inutile); // ex: p++ const NomDeLaClasse operator-() const; // ex: r = -p; //Surcharges externes const NomDeLaClasse operator+(NomDeLaClasse, NomDeLaClasse const&); // r = p + q const NomDeLaClasse operator-(NomDeLaClasse, NomDeLaClasse const&); // r = p - q ostream& operator<<(ostream&, NomDeLaClasse const&); // ex: cout << p; const NomDeLaClasse operator*(autre_type, NomDeLaClasse const&); // ex: q = x * p; Surcharges de quelques opérateurs usuels
  • 52. 5252 const Complexe operator+(Complexe, Complexe const&); z3 = z1 + z2; ++(z1 + z2); // pas de sens z1 + z2 = f(x); // idem Pourquoi const en type de retour ?
  • 53. 5353 ostream& operator<<(ostream& sortie, Complexe const& z){ cout << z1 << endl; operator<<(cout << z1, endl); operator<<(operator<<(cout, z1), endl); Pourquoi operator<< retourne-t-il un ostream& ?
  • 54. 5454 z1 += z2; void Complexe::operator+=(Complexe const&); En C++, chaque expression fait quelque chose et vaut quelque chose : x = Expression; z3 = (z1 += z2); class Complexe { // ... Complexe& operator+=(Complexe const& z2); // ... }; Complexe& Complexe::operator+=(Complexe const& z2){ real += z2.real; img += z2.real; return *this; } this étant un pointeur sur un objet,*this est l'objet lui-même ! Notre opérateur renvoie donc l'objet lui-même. Quel type de retour pour operator+= ?
  • 55. 5555 Attention de ne pas utiliser la surcharge des opérateurs à mauvais escient et à veiller à les écrire avec un soin particulier. Les performances du programme peuvent en être gravement affectées par des opérateurs surchargés mal écrits. En effet, l’utilisation inconsidérée des opérateurs peut conduire à un grand nombre de copies d’objets : Utiliser des références dès que cela est approprié ! Avertissement
  • 56. 5656 Exemple: Comparez le code suivant qui fait de 1 à 3 copies inutiles : Complexe Complexe::operator+=(Complexe z2){ Complexe z3; real += z2.real; img += z2.img; z3 = *this; return z3; } Avec le code suivant qui n’en fait pas : Complexe& Complexe::operator+=(Complexe const& z2){ real += z2.real; img += z2.img; return *this; } Avertissement
  • 57. 5757 L’opérateur d’affectation = (utilisé par exemple dans a = b) : est le seul opérateur universel (il est fourni de toutes façons par défaut pour toute classe) est très lié au constructeur de copie, sauf que le premier s’appelle lors d’une affectation et le second lors d’une initialisation la version par défaut, qui fait une copie de surface, est suffisante dans la très grande majorité des cas si nécessaire, on peut supprimer l’opérateur d’affectation : class ClasseNonAffectation { // ... private: ClasseNonAffectation& operator=(ClasseNonAffectation const&) = delete; }; Opérateur d’affectation
  • 58. 5858 Remarque: Ne confondez pas le constructeur de copie avec la surcharge de l'opérateur d’affectation = (operator=). Ils se ressemblent beaucoup mais il y a une différence : le constructeur de copie est appelé lors de l'initialisation (à la création de l'objet) tandis que la méthode operator= est appelée si on essaie d'affecter un autre objet par la suite, après son initialisation. Exemple: Complexe z1 = z2; // constructeur de copie z1 = z2; // opérateur d’affectation: operator= Opérateur d’affectation
  • 59. 5959 L’opérateur d’affectation (méthode operator=) effectue le même travail que le constructeur de copie. Si l’on doit redéfinir l’opérateur d’affectation, son implémentation est donc relativement simple: NomDeLaClasse& NomDeLaClasse::operator=(NomDeLaClasse const& source) { if (this != &source) //On vérifie que l'objet n'est pas le même que celui reçu en argument { //On copie tous les champs … } return *this; //On renvoie l'objet lui-même } Surcharge de l’opérateur d’affectation
  • 60. 6060 Depuis C++11, pour implémenter l’opérateur d’affectation =, on choisit le schéma suivant : on commencera par définir une fonction swap() pour échanger 2 objets de la classe, (sûrement en utilisant celle de la bibliothèque utility (#include <utility>) sur les attributs) puis on définira l’opérateur d’affectation comme suit : NomDeLaClasse& NomDeLaClasse::operator=(NomDeLaClasse source) // Notez le passage par VALEUR { swap(*this, source); return *this; } Surcharge de l’opérateur d’affectation
  • 62. Références 62 1) http://www.cplusplus.com 2) https://isocpp.org/ 3) https://openclassrooms.com/fr/courses/1894236-programmez-avec-le-langage-c 4) https://www.tutorialspoint.com/cplusplus/ 5) https://en.cppreference.com 6) https://stackoverflow.com/ 7) https://cpp.developpez.com/ 8) Programmer en C++, Claude Delannoy, éditions Eyrolles, 2014. 9) Initiation à la programmation (en C++), Jean-Cédric Chappelier, & Jamila Sam, coursera, 2018. 10) Introduction à la programmation orientée objet (en C++), Jamila Sam & Jean- Cédric Chappelier, coursera, 2018. 62