Les fonctions amies

Les fonctions amies

Fonction membre d’une classe B, amie d’une autre classe A class A { ….. friend — B:fct (—–) ; ….. } ; La fonction fct, membre de la classe B, ayant le prototype spécifié, est autorisée à accéder aux membres privés de la classe A. Pour qu’il puisse compiler convenablement la déclaration de A, donc en particulier la déclaration d’amitié relative à fct, le compilateur devra connaître la déclaration de B (mais pas nécessairement sa définition). Généralement, la fonction membre fct possédera un argument ou une valeur de retour de type A (ce qui justifiera sa déclaration d’amitié). Pour compiler sa déclaration (au sein de la déclaration de A), il suffira au compilateur de savoir que A est une classe ; si sa déclaration n’est pas connue à ce niveau, on pourra se contenter de : class A ; En revanche, pour compiler la définition de fct, le compilateur devra posséder les caractéristiques de A, donc disposer de sa déclaration. Toutes les fonctions d’une classe B sont amies d’une autre classe A Dans ce cas, plutôt que d’utiliser autant de déclarations d’amitié que de fonctions membre, on utilise (dans la déclaration de la classe A) la déclaration (globale) suivante : friend class B ; Pour compiler la déclaration de A, on précisera simplement que B est une classe par : class B ; Quant à la déclaration de la classe B, elle nécessitera généralement (dès qu’une de ses fonctions membre possédera un argument ou une valeur de retour de type A) la déclaration de la classe A. exos_c++. Exercice 81 Nous devons donc réaliser une fonction indépendante, nommée affiche, amie de la classe point. Une telle fonction, contrairement à une fonction membre, ne reçoit plus d’argument implicite ; affiche devra donc recevoir un argument de type point. Son prototype sera donc de la forme : void affiche (point) ; si l’on souhaite transmettre un point par valeur, ou de la forme : void affiche (point &) ; si l’on souhaite transmettre un point par référence. Ici, nous choisirons cette dernière possibilité et, comme affiche n’a aucune raison de modifier les valeurs du point reçu, nous le protégerons à l’aide du qualificatif const : void affiche (const point &) ; Le qualificatif const permet d’appliquer la fonction affiche à un objet constant. Mais elle pourra également être appliquée à une expression de type point, voire à une expression d’un type susceptible d’être converti implicitement en un point (voir le chapitre relatif aux conversions définies par l’utilisateur). Ce dernier aspect ne constitue plus nécessairement un avantage !Manifestement, affiche devra pouvoir accéder aux membres privés x et y de la classe point. Il faut donc prévoir une déclaration d’amitié au sein de cette classe, dont voici la nouvelle déclaration : /* fichier POINT1.H */ /* déclaration de la classe point */ class point { int x, y ; public : friend void affiche (const point &) ; // const non obligatoire point (int abs=0, int ord=0) { x=abs ; y=ord ; } } ; Pour écrire affiche, il nous suffit d’accéder aux membres (privés) x et y de son argument de type point. Si ce dernier se nomme p, les membres en question se notent (classiquement) p.x et p.y. Voici la définition de affiche : #include « point1.h » // nécessaire pour compiler affiche #include using namespace std ; void affiche (const point & p) { cout << « Coordonnées :  » << p.x <<  »  » << p.y << « \n » ; } Notez bien que la compilation de affiche nécessite la déclaration de la classe point, et pas seulement une déclaration telle que class point, car le compilateur doit connaître les caractéristiques de la classe point (notamment, ici, la localisation des membres x et y). Voici le petit programme d’essai demandé : #include « point1.h » main() { point a(1,5) ; affiche (a) ; point * adp ; adp = new point (2, 12) ; affiche (*adp) ; // attention *adp et non adp }

Le mécanisme de la surdéfinition d’opérateurs

Pour surdéfinir un opérateur existant op, on définit une fonction nommée operator op (on peut placer un ou plusieurs espaces entre le mot operator et l’opérateur, mais ce n’est pas une obligation) : ■ soit sous forme d’une fonction indépendante (généralement amie d’une ou de plusieurs classes) ; ■ soit sous forme d’une fonction membre d’une classe. Dans le premier cas, si op est un opérateur binaire, la notation a op b est équivalente à : operator op (a, b) exos_c++.book Page 155 Jeudi, 5. juillet 2007 11:10 11 Exercices en langage C++ 156 © Éditions Eyrolles Dans le second cas, la même notation est équivalente à : a.operator op (b) Les possibilités et les limites de la surdéfinition d’opérateurs On doit se limiter aux opérateurs existants, en conservant leur « pluralité » (unaire, binaire). Les opérateurs ainsi surdéfinis gardent leur priorité et leur associativité habituelle (voir tableau récapitulatif, un peu plus loin). Un opérateur surdéfini doit toujours posséder un opérande de type classe (on ne peut donc pas modifier les significations des opérateurs usuels). Il doit donc s’agir : ■ soit d’une fonction membre, auquel cas elle dispose obligatoirement d’un argument implicite du type de sa classe (this) ; ■ soit d’une fonction indépendante (ou plutôt amie) possédant au moins un argument de type classe. Il ne faut pas faire d’hypothèse sur la signification a priori d’un opérateur ; par exemple, la signification de += pour une classe ne peut en aucun cas être déduite de la significaiton de + et de = pour cette même classe. Cas particuliers Les opérateurs [], (), ->, new et delete doivent obligatoirement être définis comme fonctions membre. Les opérateurs = (affectation) et & (pointeur sur) possèdent une signification prédéfinie pour les objets de n’importe quel type classe. Cela ne les empêche nullement d’être surdéfinis. En ce qui concerne l’opérateur d’affectation, on peut choisir de transmettre son unique argument par valeur ou par référence. Dans le dernier cas, on ne perdra pas de vue que le seul moyen d’autoriser l’affectation d’une expression consiste à déclarer cet argument constant. La surdéfinition de new, pour un type classe donné, se fait par une fonction de prototype : void * new (size_t) Elle reçoit, en unique argument, la taille de l’objet à allouer (cet argument sera généré automatiquement par le compilateur, lors d’un appel de new), et elle doit fournir en retour l’adresse de l’objet alloué. La surdéfinition de delete, pour un type donné, se fait par une fonction de prototype : void delete (type *) Elle reçoit, en unique argument, l’adresse de l’objet à libérer.

Formation et coursTélécharger le document complet

Télécharger aussi :

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *