J'ai lu la FAQ C ++ et j'étais curieux de la friend
déclaration. Personnellement, je ne l'ai jamais utilisé, mais je suis intéressé à explorer la langue.
Quel est un bon exemple d'utilisation friend
?
En lisant la FAQ un peu plus longtemps, j'aime l'idée de la <<
>>
surcharge de l' opérateur et de l'ajouter comme ami de ces classes. Cependant, je ne sais pas comment cela ne rompt pas l'encapsulation. Quand ces exceptions peuvent-elles rester dans la rigueur de la POO?
c++
oop
encapsulation
friend
Peter Mortensen
la source
la source
Réponses:
Tout d'abord (l'OMI) n'écoute pas les gens qui disent
friend
n'est pas utile. C'est utile. Dans de nombreuses situations, vous aurez des objets avec des données ou des fonctionnalités qui ne sont pas destinées à être accessibles au public. Cela est particulièrement vrai pour les grandes bases de code avec de nombreux auteurs qui ne connaissent que superficiellement différents domaines.Il existe des alternatives au spécificateur ami, mais souvent elles sont lourdes (classes concrètes au niveau cpp / typedefs masqués) ou non infaillibles (commentaires ou conventions de nom de fonction).
Sur la réponse;
Le
friend
spécificateur permet à la classe désignée d'accéder aux données ou fonctionnalités protégées au sein de la classe faisant la déclaration friend. Par exemple, dans le code ci-dessous, n'importe qui peut demander son nom à un enfant, mais seules la mère et l'enfant peuvent changer le nom.Vous pouvez prendre cet exemple simple plus loin en considérant une classe plus complexe telle qu'une fenêtre. Il est très probable qu'une fenêtre aura de nombreux éléments de fonction / données qui ne devraient pas être accessibles au public, mais SONT nécessaires à une classe connexe telle qu'un WindowManager.
la source
friend
améliore l' encapsulation.friend
accorde un accès sélectif aux membres, tout comme leprotected
fait. Tout contrôle fin est préférable à l'octroi d'un accès public. D'autres langages définissent également des mécanismes d'accès sélectif, pensez aux C #internal
. La plupart des critiques négatives concernant l'utilisation defriend
sont liées au couplage plus étroit, qui est généralement considéré comme une mauvaise chose. Cependant, dans certains cas, un couplage plus serré est précisément ce que vous voulez etfriend
vous donne cette puissance.friend
plutôt que sur un exemple motivant . L'exemple Window / WindowManager est meilleur que l'exemple illustré, mais trop vague. Cette réponse n'aborde pas non plus la partie encapsulation de la question.Au travail, nous utilisons largement des amis pour tester le code . Cela signifie que nous pouvons fournir une encapsulation appropriée et masquer les informations pour le code d'application principal. Mais nous pouvons également avoir un code de test distinct qui utilise des amis pour inspecter l'état interne et les données à tester.
Il suffit de dire que je n'utiliserais pas le mot-clé ami comme élément essentiel de votre conception.
la source
Le
friend
mot-clé a plusieurs bonnes utilisations. Voici les deux utilisations immédiatement visibles pour moi:Définition d'ami
La définition de Friend permet de définir une fonction dans la portée de classe, mais la fonction ne sera pas définie comme une fonction membre, mais comme une fonction libre de l'espace de noms englobant, et ne sera pas visible normalement, sauf pour la recherche dépendante de l'argument. Cela le rend particulièrement utile pour la surcharge de l'opérateur:
Classe de base CRTP privée
Parfois, vous trouvez la nécessité pour une stratégie d'accéder à la classe dérivée:
Vous trouverez un exemple non artificiel pour cela dans cette réponse. Un autre code utilisant cela est dans cette réponse. La base CRTP jette son ce pointeur, pour pouvoir accéder aux champs de données de la classe dérivée en utilisant des pointeurs de membres de données.
la source
P<C>
entemplate<template<typename> class P> class C : P<C> {};
indiquant "L'utilisation du modèle de classe C nécessite des arguments de modèle". Avez-vous eu les mêmes problèmes ou connaissez-vous une solution?FlexibleClass
intérieurFlexibleClass
devrait implicitement faire référence à son propre type.@roo : L'encapsulation n'est pas interrompue ici car la classe elle-même dicte qui peut accéder à ses membres privés. L'encapsulation ne serait interrompue que si cela pouvait être causé de l'extérieur de la classe, par exemple si vous
operator <<
proclamiez «Je suis un ami de classefoo
».friend
remplace l'utilisation depublic
, pas l'utilisation deprivate
!En fait, la FAQ C ++ y répond déjà .
la source
friend
ne fait pas exception. La seule véritable observation ici est que C ++ assure l'encapsulation uniquement au moment de la compilation. Et vous n'avez plus besoin de mots pour le dire. Le reste est des conneries. Donc, en résumé: cette section de la FQA ne mérite pas d'être mentionnée.L'exemple canonique est de surcharger l'opérateur <<. Une autre utilisation courante consiste à autoriser un assistant ou une classe d'administration à accéder à vos éléments internes.
Voici quelques directives que j'ai entendues sur les amis C ++. Le dernier est particulièrement mémorable.
la source
friend
je suppose.Comment romprait-elle l'encapsulation?
Vous rompez l'encapsulation lorsque vous autorisez un accès illimité à un membre de données. Considérez les classes suivantes:
c1
n'est évidemment pas encapsulé. Tout le monde peut y lire et le modifierx
. Nous n'avons aucun moyen d'imposer un type de contrôle d'accès.c2
est évidemment encapsulé. Il n'y a pas d'accès public àx
. Tout ce que vous pouvez faire est d'appeler lafoo
fonction, qui effectue une opération significative sur la classe .c3
? Est-ce moins encapsulé? Permet-il un accès illimité àx
? Permet-il l'accès à des fonctions inconnues?Non. Il permet précisément à une fonction d'accéder aux membres privés de la classe. Tout comme l'a
c2
fait. Et tout commec2
, la seule fonction qui a accès n'est pas «une fonction inconnue aléatoire», mais «la fonction répertoriée dans la définition de classe». Tout commec2
, nous pouvons voir, juste en regardant les définitions de classe, une liste complète de qui a accès.Alors, comment est-ce exactement moins encapsulé? La même quantité de code a accès aux membres privés de la classe. Et tous ceux qui y ont accès sont répertoriés dans la définition de classe.
friend
ne rompt pas l'encapsulation. Cela met certains programmeurs Java mal à l'aise, car lorsqu'ils disent "OOP", ils signifient en fait "Java". Quand ils disent "encapsulation", ils ne signifient pas "les membres privés doivent être protégés contre les accès arbitraires", mais "une classe Java où les seules fonctions capables d'accéder aux membres privés, sont des membres de classe", même si cela n'a aucun sens pour plusieurs raisons .Premièrement, comme déjà montré, c'est trop restrictif. Il n'y a aucune raison pour que les méthodes amis ne soient pas autorisées à faire de même.
Deuxièmement, ce n'est pas assez restrictif . Considérons une quatrième classe:
Ceci, selon la mentalité Java précitée, est parfaitement encapsulé. Et pourtant, cela permet à quiconque de lire et de modifier x . Comment cela a-t-il même un sens? (indice: ce n'est pas le cas)
Conclusion: l'encapsulation consiste à pouvoir contrôler les fonctions pouvant accéder aux membres privés. Il ne s'agit pas précisément de l'emplacement des définitions de ces fonctions.
la source
Une autre version courante de l'exemple d'Andrew, le redouté couplet de code
Au lieu de vous inquiéter si les deux lignes sont toujours effectuées ensemble et dans un ordre cohérent, vous pouvez rendre les méthodes privées et avoir une fonction amie pour appliquer la cohérence:
En d'autres termes, vous pouvez réduire la taille des interfaces publiques et appliquer des invariants qui traversent les classes et les objets dans les fonctions ami.
la source
addChild
fonction membre définir également le parent?setParent
un ami, car vous ne voulez pas autoriser les clients à changer le parent puisque vous le gérerez dans la catégorieaddChild
/removeChild
des fonctions.Vous contrôlez les droits d'accès des membres et des fonctions en utilisant le droit Privé / Protégé / Public? donc en supposant que l'idée de chacun de ces 3 niveaux est claire, alors il devrait être clair que nous manquons quelque chose ...
La déclaration d'un membre / fonction comme protégé par exemple est assez générique. Vous dites que cette fonction est hors de portée pour tout le monde (sauf pour un enfant hérité bien sûr). Mais qu'en est-il des exceptions? chaque système de sécurité vous permet d'avoir une sorte de «liste blanche», n'est-ce pas?
Alors, ami, vous avez la possibilité d'avoir une isolation solide contre les objets, mais permet de créer une "faille" pour les choses que vous jugez justifiées.
Je suppose que les gens disent que ce n'est pas nécessaire car il y a toujours un design qui s'en passera. Je pense que c'est similaire à la discussion des variables globales: vous ne devriez jamais les utiliser, il y a toujours un moyen de s'en passer ... mais en réalité, vous voyez des cas où cela finit par être le moyen (presque) le plus élégant. .. Je pense que c'est le même cas avec des amis.
eh bien ce n'est pas exactement la façon de voir les choses. L’idée est de contrôler l’OMS qui peut accéder à quoi, ayant ou non fonction de réglage n'a pas grand-chose à voir avec cela.
la source
friend
une échappatoire? Il permet aux méthodes répertoriées dans la classe d' accéder à ses membres privés. Il ne laisse toujours pas de code arbitraire y accéder. En tant que tel, il n'est pas différent d'une fonction de membre public.J'ai trouvé un endroit pratique pour utiliser l'accès ami: la plus petite des fonctions privées.
la source
Friend est utile lorsque vous créez un conteneur et que vous souhaitez implémenter un itérateur pour cette classe.
la source
Nous avons eu un problème intéressant dans une entreprise où je travaillais auparavant, où nous avons utilisé un ami pour un effet décent. J'ai travaillé dans notre service de cadre, nous avons créé un système de niveau moteur de base sur notre système d'exploitation personnalisé. En interne, nous avions une structure de classe:
Toutes ces classes faisaient partie du cadre et maintenues par notre équipe. Les jeux produits par la société ont été construits au-dessus de ce cadre dérivé d'un des enfants des Jeux. Le problème était que Game avait des interfaces avec diverses choses auxquelles SinglePlayer et TwoPlayer avaient besoin d'accéder mais que nous ne voulions pas exposer en dehors des classes de framework. La solution consistait à rendre ces interfaces privées et à permettre à TwoPlayer et SinglePlayer d'y accéder via l'amitié.
En vérité, tout ce problème aurait pu être résolu par une meilleure mise en œuvre de notre système, mais nous étions enfermés dans ce que nous avions.
la source
La réponse courte serait: utilisez ami lorsque cela améliore réellement l' encapsulation. L'amélioration de la lisibilité et de l'utilisabilité (les opérateurs << et >> sont l'exemple canonique) est également une bonne raison.
En ce qui concerne les exemples d'amélioration de l'encapsulation, les classes spécialement conçues pour fonctionner avec les internes d'autres classes (les classes de test me viennent à l'esprit) sont de bons candidats.
la source
<<
et>>
sont généralement amis, au lieu de membres, car les rendre membres les rendrait difficiles à utiliser. Bien sûr, je parle du cas où ces opérateurs ont besoin d'accéder à des données privées; sinon, l'amitié est inutile.operator<<
et lesoperator>>
membres de la classe de valeur au lieu de non-membres, ou membres dei|ostream
, ne fourniraient pas la syntaxe souhaitée, et je ne le suggère pas. " Je parle du cas où ces opérateurs ont besoin d'accéder à des données privées " Je ne vois pas très bien pourquoi les opérateurs d'entrée / sortie auraient besoin d'accéder à des membres privés.Le créateur de C ++ dit que cela ne rompt aucun principe d'encapsulation, et je vais le citer:
C'est plus que clair ...
la source
Une autre utilisation: ami (+ héritage virtuel) peut être utilisé pour éviter de dériver d'une classe (aka: "rendre une classe insécable") => 1 , 2
À partir de 2 :
la source
Pour faire TDD plusieurs fois, j'ai utilisé le mot-clé «ami» en C ++.
Un ami peut-il tout savoir sur moi?
Mise à jour: j'ai trouvé cette précieuse réponse sur le mot-clé "ami" du site Bjarne Stroustrup .
la source
Vous devez être très prudent quand / où vous utilisez le
friend
mot - clé et, comme vous, je l'ai utilisé très rarement. Voici quelques notes sur l'utilisationfriend
et les alternatives.Disons que vous voulez comparer deux objets pour voir s'ils sont égaux. Vous pouvez soit:
Le problème avec la première option, c'est que cela pourrait être BEAUCOUP d'accessoires, qui est (légèrement) plus lent que l'accès variable direct, plus difficile à lire et encombrant. Le problème avec la deuxième approche est que vous rompez complètement l'encapsulation.
Ce qui serait bien, c'est si nous pouvions définir une fonction externe qui pourrait toujours avoir accès aux membres privés d'une classe. Nous pouvons le faire avec le
friend
mot clé:La méthode
equal(Beer, Beer)
a un accès direct maintenanta
etb
membres privés »(qui peut êtrechar *brand
,float percentAlcohol
etc. Ceci est un exemple plutôt arrangé, vous appliqueriez plus tôtfriend
à une surcharge== operator
, mais nous y reviendrons.Quelques points à noter:
friend
n'est PAS une fonction membre de la classepublic
!)Je ne l'utilise vraiment que
friends
lorsqu'il est beaucoup plus difficile de le faire dans l'autre sens. Comme autre exemple, beaucoup de mathématiques vecteur fonctions sont souvent créés enfriends
raison de l'interopérabilitéMat2x2
,Mat3x3
,Mat4x4
,Vec2
,Vec3
,Vec4
, etc. Et il est tellement plus facile d'être amis, plutôt que d' avoir à utiliser accesseurs partout. Comme indiqué,friend
est souvent utile lorsqu'il est appliqué à<<
(vraiment pratique pour le débogage),>>
et peut-être à l'==
opérateur, mais peut également être utilisé pour quelque chose comme ceci:Comme je l'ai dit, je n'utilise pas du
friend
tout très souvent, mais de temps en temps c'est exactement ce dont vous avez besoin. J'espère que cela t'aides!la source
En ce qui concerne l'opérateur << et l'opérateur >>, il n'y a aucune bonne raison de se faire des amis. Il est vrai qu'ils ne devraient pas être des fonctions membres, mais ils n'ont pas non plus besoin d'être amis.
La meilleure chose à faire est de créer des fonctions publiques d'impression (ostream &) et de lecture (istream &). Ensuite, écrivez l'opérateur << et l'opérateur >> en fonction de ces fonctions. Cela donne l'avantage supplémentaire de vous permettre de rendre ces fonctions virtuelles, ce qui permet une sérialisation virtuelle.
la source
J'utilise uniquement le mot-clé friend pour les fonctions protégées les plus complètes. Certains diront que vous ne devriez pas tester la fonctionnalité protégée. Je trouve cependant cet outil très utile lors de l'ajout de nouvelles fonctionnalités.
Cependant, je n'utilise pas le mot clé directement dans les déclarations de classe, j'utilise plutôt un astucieux modèle-hack pour y parvenir:
Cela me permet de faire ce qui suit:
Fonctionne sur GCC et MSVC au moins.
la source
En C ++, le mot-clé "friend" est utile dans la surcharge de l'opérateur et la création d'un pont.
Maintenant, la première option n'est pas bonne parce que si nous devons surcharger à nouveau cet opérateur pour une classe différente, nous devons à nouveau faire des changements dans la classe "ostream".1.) Mot-clé ami dans la surcharge d'opérateur:
Exemple de surcharge d'opérateur: Supposons que nous ayons une classe "Point" qui a deux variables flottantes
"x" (pour la coordonnée x) et "y" (pour la coordonnée y). Maintenant, nous devons surcharger
"<<"
(opérateur d'extraction) de telle sorte que si nous appelons"cout << pointobj"
il affichera les coordonnées x et y (où pointobj est un objet de la classe Point). Pour ce faire, nous avons deux options:C'est pourquoi la seconde est la meilleure option. Le compilateur peut maintenant appeler la
"operator <<()"
fonction:Parce que nous avons implémenté la surcharge dans la classe Point. Donc, pour appeler cette fonction sans objet, nous devons ajouter un
"friend"
mot-clé car nous pouvons appeler une fonction amie sans objet. Maintenant, la déclaration de fonction sera As:"friend ostream &operator<<(ostream &cout, Point &pointobj);"
2.) Mot-clé Friend dans la création d'un pont:
supposons que nous devons créer une fonction dans laquelle nous devons accéder à un membre privé de deux classes ou plus (généralement appelé "pont"). Comment faire:
pour accéder au membre privé d'une classe, il doit être membre de cette classe. Maintenant, pour accéder à un membre privé d'une autre classe, chaque classe doit déclarer cette fonction comme une fonction amie. Par exemple: Supposons qu'il existe deux classes A et B. Une fonction
"funcBridge()"
souhaite accéder au membre privé des deux classes. Ensuite, les deux classes doivent déclarer"funcBridge()"
:friend return_type funcBridge(A &a_obj, B & b_obj);
Je pense que cela aiderait à comprendre le mot-clé ami.
la source
Comme le dit la référence pour la déclaration d'ami :
Donc, juste pour rappel, il y a des erreurs techniques dans certaines des réponses qui disent que
friend
seuls les membres protégés peuvent visiter .la source
L'exemple d'arbre est un très bon exemple: avoir un objet implémenté dans quelques classes différentes sans avoir de relation d'héritage.
Peut-être pourriez-vous également en avoir besoin pour protéger un constructeur et forcer les gens à utiliser votre usine "ami".
... Ok, bien franchement vous pouvez vous en passer.
la source
Non, c'est seulement une amitié à sens unique: `(
la source
Une instance spécifique où j'utilise
friend
est lors de la création de classes Singleton . Lefriend
mot-clé me permet de créer une fonction d'accesseur, qui est plus concise que d'avoir toujours une méthode "GetInstance ()" sur la classe.la source
friend
n'a pas besoin d'une "justification" particulière , contrairement à l'ajout d'une fonction membre.Les fonctions et classes Friend fournissent un accès direct aux membres privés et protégés de la classe pour éviter de briser l'encapsulation dans le cas général. La plupart des utilisations se font avec ostream: nous aimerions pouvoir taper:
Cependant, cela peut nécessiter l'accès aux données privées de Point, nous définissons donc l'opérateur surchargé
Il y a cependant des implications évidentes d'encapsulation. Tout d'abord, la classe ou la fonction ami a désormais un accès complet à TOUS les membres de la classe, même ceux qui ne correspondent pas à ses besoins. Deuxièmement, les implémentations de la classe et de l'ami sont désormais imbriquées au point où un changement interne dans la classe peut briser l'ami.
Si vous voyez l'ami comme une extension de la classe, ce n'est pas un problème, logiquement. Mais, dans ce cas, pourquoi était-il nécessaire de spearer l'ami en premier lieu.
Pour atteindre la même chose que les «amis» prétendent accomplir, mais sans rompre l'encapsulation, on peut le faire:
L'encapsulation n'est pas rompue, la classe B n'a pas accès à l'implémentation interne dans A, mais le résultat est le même que si nous avions déclaré B ami de A. Le compilateur optimisera les appels de fonction, ce qui entraînera la même chose instructions comme accès direct.
Je pense que l'utilisation de «ami» est simplement un raccourci avec des avantages discutables, mais un coût certain.
la source
Il ne s'agit peut-être pas d'une situation d'utilisation réelle, mais cela peut aider à illustrer l'utilisation d'amis entre les classes.
The ClubHouse
La classe des membres
Agréments
Si vous regardez la relation de ces classes ici; le ClubHouse détient une variété de différents types d'adhésions et d'accès aux membres. Les membres sont tous dérivés d'une classe super ou de base car ils partagent tous un ID et un type énuméré qui sont des classes communes et externes peuvent accéder à leurs ID et types via des fonctions d'accès qui se trouvent dans la classe de base.
Cependant, à travers ce type de hiérarchie des membres et de ses classes dérivées et de leur relation avec la classe ClubHouse, la seule classe dérivée qui a des "privilèges spéciaux" est la classe VIPMember. La classe de base et les 2 autres classes dérivées ne peuvent pas accéder à la méthode joinVIPEvent () de ClubHouse, mais la classe Membre VIP a ce privilège comme si elle avait un accès complet à cet événement.
Ainsi, avec le VIPMember et le ClubHouse, il s'agit d'une rue d'accès à double sens où les autres classes membres sont limitées.
la source
Lors de l'implémentation d'algorithmes d'arbre pour la classe, le code cadre que le prof nous a donné avait la classe d'arbre en tant qu'ami de la classe de noeud.
Cela ne fait pas vraiment de bien, à part vous permettre d'accéder à une variable membre sans utiliser de fonction de réglage.
la source
Vous pouvez utiliser l'amitié lorsque différentes classes (n'héritant pas l'une de l'autre) utilisent des membres privés ou protégés de l'autre classe.
sur http://www.cplusplus.com/doc/tutorial/inheritance/ .
Vous pouvez voir cet exemple où la méthode non membre accède aux membres privés d'une classe. Cette méthode doit être déclarée dans cette même classe en tant qu'ami de la classe.
la source
J'ai probablement manqué quelque chose dans les réponses ci-dessus, mais un autre concept important dans l'encapsulation cache la mise en œuvre. La réduction de l'accès aux données privées membres (les détails d'implémentation d'une classe) permet une modification beaucoup plus facile du code ultérieurement. Si un ami accède directement aux données privées, toute modification des champs de données d'implémentation (données privées), casse le code d'accès à ces données. L'utilisation de méthodes d'accès élimine principalement cela. Assez important, je pense.
la source
Vous pouvez adhérer aux principes de POO les plus stricts et les plus purs et vous assurer qu'aucun membre de données pour aucune classe n'a même des accesseurs de sorte que tous les objets doivent être les seuls à pouvoir connaître leurs données avec la seule façon d'agir sur eux via des messages indirects , c'est-à-dire les méthodes.
Mais même C # a un mot-clé de visibilité interne et Java a son accessibilité au niveau du package par défaut pour certaines choses. C ++ se rapproche en fait de l'idéal OOP en minimisant le compromis de visibilité dans une classe en spécifiant exactement quelle autre classe et seulement d' autres classes pourraient y voir.
Je n'utilise pas vraiment C ++ mais si C # avait des amis, je le ferais au lieu du modificateur interne global d'assemblage , que j'utilise beaucoup. Cela ne rompt pas vraiment l'incapsulation, car l'unité de déploiement dans .NET est un assemblage.
Mais il y a ensuite l' attribut InternalsVisibleTo (otherAssembly) qui agit comme un mécanisme ami de l' assemblage croisé . Microsoft l'utilise pour les assemblages de concepteurs visuels .
la source
Les amis sont également utiles pour les rappels. Vous pouvez implémenter des rappels en tant que méthodes statiques
où les
callback
appels enlocalCallback
interne, et leclientData
contient votre instance. À mon avis,ou...
Cela permet à l'ami d'être défini uniquement dans le cpp en tant que fonction de style c, et non d'encombrer la classe.
De même, un modèle que j'ai vu très souvent est de mettre tous les membres vraiment privés d'une classe dans une autre classe, qui est déclarée dans l'en-tête, définie dans le cpp et friended. Cela permet au codeur de cacher une grande partie de la complexité et du fonctionnement interne de la classe à l'utilisateur de l'en-tête.
Dans l'en-tête:
Dans le cpp,
Il devient plus facile de cacher des choses que l'aval n'a pas besoin de voir de cette façon.
la source