Fonctions amies

Une fonction est l’amie (friend) d’une classe lorsqu’elle est autorisée à adresser directement les membres privés de cette classe. Pour la déclarer ainsi, il faut donner, à l’intérieur de la classe, la déclaration complète de la fonction précédée du mot clé friend. Voici un exemple simple :

class exemple {     int i, j;     public:     exemple() { i = 0; j = 0; }     friend exemple inverse(exemple);     };				exemple inverse(exemple ex)// renvoie ex avec tous les bits inversés{     exemple ex2 = ex;     ex2.i = ~ex2.i;        // accès aux champs     ex2.j = ~ex2.j;     return ex;}

À part le fait qu’elle est amie de la classe exemple, la fonction est parfaitement ordinaire, et peut être déclarée et définie de la même façon que toute autre.

Le terme même d’amie indique clairement que la fonction doit avoir un comportement décent : il faut veiller à ce qu’elle ne modifie pas incorrectement les membres de la classe.

Une fonction peut être amie d’autant de classes que nécessaire, mais évidemment cela n’est utile que lorsque la fonction utilise une instance de la classe, et plus précisément modifie un membre privé de la classe (car en général il existe des fonctions membres en ligne permettant de lire ces membres privés ou une interprétation de ceux-ci).

Notons que la « déclaration d’amitié » doit se faire à l’intérieur de la classe. De ce fait, si l’on dispose d’une classe mais sans avoir la possibilité de la modifier (par exemple, dans un fichier en-tête on peut ne trouver que la déclaration d’une classe sans sa définition), il n’est pas possible de lui ajouter des fonctions amies. Cela n’est pas une restriction du langage, mais au contraire un moyen sûr et efficace de protéger des données. De ce fait, avant de « verrouiller » une classe, on prendra soin de fournir tous les moyens d’accès raisonnables (en lecture notamment) aux champs utiles, afin de permettre la création de fonctions non amies utilisant cette classe.

Lorsque cette précaution a été prise, il n’est plus besoin d’une fonction amie, en dépit des apparences. Par exemple, la librairie <complex.h> fournit une classe complex (qui est fondamentalement formée de deux nombres à virgule flottante nommés partie réelle et partie imaginaire) et un ensemble de fonctions la manipulant ; cependant, les concepteurs de la librairie n’ont pas implanté une opération importante sur les nombres complexes, nommée conjugaison, qui consiste simplement à changer le signe de la partie imaginaire. Est-ce à dire qu’il faut modifier <complex.h> pour déclarer « amie » la fonction ayant cet effet ? Nullement, car on dispose de deux fonctions amies real et imag donnant les parties réelle et imaginaire d’un complexe, ainsi que du constructeur complex(double, double) qui crée un complexe à partir de ses deux parties. De ce fait, il suffit d’écrire une fonction normale :

inline complexe conjug(complexe c){     return complexe(real(c), -imag(c));}

Cette fonction n’est pas amie de la classe complex, mais elle n’accède qu’à des parties publiques de celle-ci (le constructeur et les deux fonctions amies real et imag), il n’y a donc pas de problème. On pourrait bien sûr s’inquiéter : les trois appels de fonction (real, imag et complex) ne vont-ils pas grever le temps d’exécution de cette opération pourtant élémentaire ? Nullement, car ces trois fonctions très simples aussi sont écrites en ligne. De ce fait, l’écriture c1 = conjug(c2) ; ne provoquera aucun appel de fonction, puisque conjug est aussi en ligne.

Précédent Précédent Sommaire Sommaire Suivant Suivant