Vous savez créer des classes mères, des classes filles, des classes petites-filles, etc. Un vrai arbre généalogique en quelque sorte. Mais en POO, comme dans la vie, il n'y a pas que la famille, il y a aussi les amis.
Qu'est-ce que l'amitié ?
L'amitié dans les langages orientés objet est le fait de donner un accès complet aux éléments d'une classe.
Donc si je déclare une fonction f amie de la classe A, la fonction f pourra modifier les attributs de la classe A
même si les attributs sont privés ou protégés. La fonction f pourra également utiliser les fonctions privées et protégées de la classe A.
On dit alors que la
fonction f est amie de la classe A.
En déclarant une fonction amie d'une classe, on casse complètement l'encapsulation de la classe puisqu'un être externe à la classe pourra modifier ce qu'il y a dedans.
Il ne faut donc pas abuser de l'amitié.
Je vous ai expliqué dès le début que l'encapsulation était l'élément le plus important en POO et voilà que je vous présente un moyen de détourner ce concept. Je suis d'accord avec vous, c'est assez paradoxal. Pourtant, utiliser à
bon escient l'amitié peut renforcer l'encapsulation. Voyons comment !
Retour sur la classe Duree
Pour vous présenter la surcharge des opérateurs, j'ai utilisé la classe
Duree dont le but était de représenter la notion d'intervalle de temps. Voici le prototype de la classe :
Code : C++ - Prototype de la classe Duree 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 | class Duree
{
public:
Duree(int heures = 0, int minutes = 0, int secondes = 0);
void affiche(ostream& out) const; //Permet d'écrire la durée dans un flux
private:
int m_heures;
int m_minutes;
int m_secondes;
};
//Surcharge de l'opérateur << pour l'écriture dans les flux
//Utilise la méthode affiche() de Duree
ostream &operator<<( ostream &out, Duree const& duree );
|
Je ne vous ai mis que l'essentiel. Il y avait bien plus d'opérateurs déclarés à la fin du chapitre. Ce qui va nous intéresser, c'est la surcharge de l'opérateur d'injection dans les flux. Voici ce que nous avions écrit :
Code : C++ - Surcharge de l'opérateur << | ostream &operator<<( ostream &out, Duree const& duree )
{
duree.affiche(out) ;
return out;
}
|
Et c'est très souvent la meilleure solution ! Mais pas toujours... En effet, en faisant ça, vous avez besoin d'écrire une méthode
affiche() dans la classe. C'est-à-dire, que votre classe va fournir un service supplémentaire.
Vous allez ajouter un levier en plus en surface de votre classe.
Sauf que ce levier n'est destiné qu'à l'opérateur
<< et pas au reste du monde. Il y a donc une méthode dans la classe qui d'une certaine manière ne sert à rien pour un utilisateur normal.
Dans ce cas, cela ne porte pas vraiment à conséquence. Si quelqu'un utilise la méthode
affiche(), alors rien de dangereux pour l'objet ne se passe. Il s'écrit juste dans la console ou dans un fichier. Mais dans d'autres cas, il pourrait être dangereux d'avoir une méthode qu'il ne faut surtout pas utiliser.
C'est comme dans les laboratoires, si vous avez un gros bouton rouge avec un écriteau indiquant "Ne surtout pas appuyer", vous pouvez être sûr que quelqu'un va, un jour, faire l'erreur d'appuyer dessus.
Le mieux serait donc de ne pas laisser apparaître ce levier en surface de notre cube-objet. Ce qui revient à mettre la méthode
affiche() dans la partie privée de la classe.
Code : C++ - Prototype de la classe Duree 1
2
3
4
5
6
7
8
9
10
11
12
13
14 | class Duree
{
public:
Duree(int heures = 0, int minutes = 0, int secondes = 0);
private:
void affiche(ostream& out) const; //Permet d'écrire la durée dans un flux
int m_heures;
int m_minutes;
int m_secondes;
};
|
En faisant cela, plus de risque d'appeler la méthode par erreur. Par contre, l'opérateur
<< ne peut plus, lui non plus, l'utiliser.
C'est là que l'amitié intervient. Si l'opérateur
<< est déclaré ami de la classe
Duree, il aura accès à la partie privée de la classe et par conséquent à la méthode
affiche().
Déclarer une fonction amie d'une classe
Interro surprise d'anglais. Comment dit-on « ami » en anglais ?
Friend, exactement ! Et comme les créateurs du C++ ne voulaient pas se casser la tête avec les noms compliqués, ils ont pris
friend comme mot-clé pour l'amitié. D'ailleurs si vous tapez ce mot dans votre IDE, il devrait s'écrire d'une couleur différente. C'est bien la preuve.
Pour déclarer une fonction amie d'une classe, on utilise la syntaxe suivante :
Code : C++ | friend std::ostream& operator<< (std::ostream& flux, Duree const& duree);
|
On écrit
friend suivi du prototype de la fonction. Et on place le tout à l'intérieur de la classe.
Code : C++ - Prototype de la classe Duree 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 | class Duree
{
public:
Duree(int heures = 0, int minutes = 0, int secondes = 0);
private:
void affiche(ostream& out) const; //Permet d'écrire la durée dans un flux
int m_heures;
int m_minutes;
int m_secondes;
friend std::ostream& operator<< (std::ostream& flux, Duree const& duree);
};
|
Vous pouvez mettre le prototype de la fonction dans la partie publique, protégée ou privée de la classe, cela n'a aucune importance.
Notre opérateur
<< a maintenant accès à tout ce qui se trouve dans la classe
Duree sans aucune restriction. Il peut donc en particulier utiliser la méthode
affiche(), comme précédemment. Sauf que désormais, c'est le seul élément hors de la classe qui peut utiliser cette méthode.
On peut utiliser la même astuce pour les opérateurs == et <. En les déclarant comme amis de la classe
Duree, ces fonctions pourront accéder directement aux attributs et l'on peut alors supprimer les méthodes
estPlusPetitQue() et
estEgal(). Je vous laisse essayer ...
L'amitié et la responsabilité
Être l'ami de quelqu'un a certaines conséquences en matière de savoir-vivre. Je présume que vous n'allez pas chez vos amis à 3h du matin pour saccager leur jardin pendant leur sommeil.
En C++, l'amitié implique également que la fonction amie ne viendra pas détruire la classe et ne viendra pas non plus saccager les attributs de la classe. Si vous avez besoin d'une fonction qui doit modifier grandement le contenu d'une classe, alors faites plutôt une fonction membre de la classe.
Vos programmes devraient respecter les deux règles suivantes :
- Une fonction amie ne devrait, en principe, pas modifier l'instance de la classe.
- Utilisez les fonctions amies que si vous ne pouvez pas faire autrement.
Cette deuxième règle est très importante. Si vous ne la respectez pas, alors autant arrêter la POO, car le concept de classe perd tout son sens.