Quand devriez-vous utiliser 'friend' en C++?

J'ai lu la FAQ C++ et j'étais curieux de savoir 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 de friend?


Lire la FAQ un peu plus longtemps j'aime l'idée de la << >> surcharge de l'opérateur et ajout en tant qu'ami de ces classes. Cependant, je ne suis pas sûr de savoir comment cela ne casse pas l'encapsulation. Quand ces exceptions peuvent-elles rester dans le la rigueur c'est de la POO?

322
demandé sur Peter Mortensen 2008-08-20 09:29:32

30 réponses

Premièrement (IMO) n'écoutez pas les gens qui disent que friend n'est pas utile. Il 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 sont peut-être que superficiellement familiers avec différents domaines.

Il existe des alternatives au spécificateur d'ami, mais souvent elles sont lourdes (classes concrètes au niveau du cpp / typedefs masqués) ou non infaillibles (commentaires ou conventions de nom de fonction).

Sur la réponse;

Le spécificateur friend permet à la classe désignée d'accéder aux données ou aux fonctionnalités protégées de la classe effectuant l'instruction 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 comme une fenêtre. Très probablement, une fenêtre aura de nombreux éléments de fonction/données cela ne devrait pas être accessible au public, mais est nécessaire par une classe associée telle Qu'un WindowManager.

class Child
{
//Mother class members can access the private parts of class Child.
friend class Mother;

public:

  string name( void );

protected:

  void setName( string newName );
};
307
répondu Andrew Grant 2017-01-17 14:09:28

Au travail, nous utilisons des amis pour tester le code, largement. Cela signifie que nous pouvons fournir une encapsulation et une dissimulation d'informations appropriées pour le code d'application principal. Mais nous pouvons également avoir un code de test séparé qui utilise des amis pour inspecter l'état interne et les données pour les tests.

Autant dire que je n'utiliserais pas le mot-clé friend comme un composant essentiel de votre conception.

143
répondu Daemin 2017-02-12 10:37:11

Le mot clé friend a un certain nombre de bonnes utilisations. Voici les deux utilisations immédiatement visibles pour moi:

Définition D'Ami

La définition D'ami permet de définir une fonction dans class-scope, 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:

namespace utils {
    class f {
    private:
        typedef int int_type;
        int_type value;

    public:
        // let's assume it doesn't only need .value, but some
        // internal stuff.
        friend f operator+(f const& a, f const& b) {
            // name resolution finds names in class-scope. 
            // int_type is visible here.
            return f(a.value + b.value);
        }

        int getValue() const { return value; }
    };
}

int main() {
    utils::f a, b;
    std::cout << (a + b).getValue(); // valid
}

Base CRTP privée Classe

Parfois, vous trouvez qu'une stratégie a besoin d'accéder à la classe dérivée:

// possible policy used for flexible-class.
template<typename Derived>
struct Policy {
    void doSomething() {
        // casting this to Derived* requires us to see that we are a 
        // base-class of Derived.
        some_type const& t = static_cast<Derived*>(this)->getSomething();
    }
};

// note, derived privately
template<template<typename> class SomePolicy>
struct FlexibleClass : private SomePolicy<FlexibleClass> {
    // we derive privately, so the base-class wouldn't notice that, 
    // (even though it's the base itself!), so we need a friend declaration
    // to make the base a friend of us.
    friend class SomePolicy<FlexibleClass>;

    void doStuff() {
         // calls doSomething of the policy
         this->doSomething();
    }

    // will return useful information
    some_type getSomething();
};

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 lance son pointeur this, Pour pouvoir accéder aux champs de données de la classe dérivée en utilisant des pointeurs de membre de données.

87
répondu Johannes Schaub - litb 2017-05-23 12:18:25

@roo : L'Encapsulation n'est pas cassée ici car la classe elle-même dicte qui peut accéder à ses membres privés. L'Encapsulation ne serait cassée que si cela pouvait être causé de l'extérieur de la classe, par exemple si votre operator << proclamait "je suis un ami de la classe foo."

friend remplace l'utilisation de public, ne pas utiliser de private!

En fait, la FAQ C++ répond déjà à cela .

39
répondu Konrad Rudolph 2017-05-23 12:34:45

L'exemple canonique est de surcharger l'opérateur

Voici quelques lignes directrices que j'ai entendu parler des amis c++. Le dernier est particulièrement mémorable.

  • vos amis ne sont pas les amis de votre enfant.
  • Les Amis de votre enfant ne sont pas vos amis.
  • Seuls les amis peuvent toucher vos parties intimes.
27
répondu Mark Harrison 2008-08-20 07:05:04

Edit: en lisant la faq un peu plus longtemps, j'aime l'idée de la surcharge de l'opérateur > et de l'ajout en tant qu'ami de ces classes, mais je ne sais pas comment cela ne casse pas l'encapsulation

Comment briser l'encapsulation?

Vous interrompez l'encapsulation lorsque vous autorisez un accès illimité à un membre de données. Considérez les classes suivantes:

class c1 {
public:
  int x;
};

class c2 {
public:
  int foo();
private:
  int x;
};

class c3 {
  friend int foo();
private:
  int x;
};

c1 est , évidemment, non encapsulées. Tout le monde peut lire et modifier x dedans. Nous n'avons aucun moyen d'imposer un quelconque 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 la fonction foo, qui effectue une opération significative sur la classe.

c3? C'est que de moins en moins encapsulé? Permet-il un accès illimité à x? Permet-il l'accès aux fonctions inconnues?

Non. Il permet précisément une fonction d'accéder aux membres privés de la classe. Tout comme c2 faire. Et tout comme c2, la fonction qui a accès n'est pas "une fonction aléatoire et inconnue", mais "la fonction listée dans la définition de classe". Tout comme c2, 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 Tout le monde {[21] } qui a accès est répertorié dans la définition de classe.

friend n' pas briser l'encapsulation. Cela rend certains programmeurs Java people mal à l'aise, car quand ils disent "POO", 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 c'est un non-sens complet pour plusieurs raisons.

Tout d'abord, comme déjà montré, c'est trop restrictif. Il n'y a pas raison pour laquelle les méthodes d'ami ne devraient pas être autorisées à faire la même chose.

Deuxièmement, il n'est pas restrictif assez . Considérons une quatrième classe:

class c4 {
public:
  int getx();
  void setx(int x);
private:
  int x;
};

Ceci, selon la mentalité Java susmentionnée, est parfaitement encapsulé. et pourtant, il permet absolument à tout le monde de lire et de modifier x . Comment est-ce que même un sens? (indice: ce n'est pas le cas)

Ligne de fond: L'Encapsulation consiste à pouvoir contrôler quelles fonctions peuvent accéder aux membres privés. Il est Pas sur précisément où se trouvent les définitions de ces fonctions.

16
répondu jalf 2009-09-07 09:24:47

Une autre version commune de L'exemple D'Andrew, le redoutable code-couplet

parent.addChild(child);
child.setParent(parent);

Au lieu de vous inquiéter si les deux lignes sont toujours faites ensemble et dans un ordre cohérent, vous pouvez rendre les méthodes privées et avoir une fonction d'ami pour appliquer la cohérence:

class Parent;

class Object {
private:
    void setParent(Parent&);

    friend void addChild(Parent& parent, Object& child);
};

class Parent : public Object {
private:
     void addChild(Object& child);

     friend void addChild(Parent& parent, Object& child);
};

void addChild(Parent& parent, Object& child) {
    if( &parent == &child ){ 
        wetPants(); 
    }
    parent.addChild(child);
    child.setParent(parent);
}

En d'autres termes, vous pouvez garder les interfaces publiques plus petites et appliquer des invariants qui coupent les classes et les objets dans les fonctions d'ami.

10
répondu maccullt 2008-09-10 02:52:54

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 de 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 avoir un certain type de" liste blanche " droite?

Donc friend vous permet d'avoir la flexibilité d'avoir une isolation d'objet solide, mais permet de créer une" échappatoire " pour les choses que vous jugez justifiées.

Je suppose que les gens disent que ce n'est pas nécessaire parce qu'il y a toujours un design qui va s'en passer. 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 la manière (presque) la plus élégante... Je pense que c'est le même cas avec des amis.

Cela ne fait pas vraiment de bien, à part vous permettre d'accéder à une variable membre sans utiliser une fonction de réglage

Eh bien, ce n'est pas exactement la façon de le regarder. L'idée est de contrôler qui peut accéder à quoi, avoir ou non une fonction de réglage a peu à voir avec cela.

8
répondu csmba 2008-08-20 06:10:58

J'ai trouvé un endroit pratique pour utiliser friend access: Unittest des fonctions privées.

8
répondu VladimirS 2015-02-19 19:34:00

Friend est pratique lorsque vous construisez un conteneur et que vous souhaitez implémenter un itérateur pour cette classe.

5
répondu rptony 2016-01-26 16:55:28

La réponse courte serait: utilisez friend quand elle améliore réellement l'encapsulation. Améliorer la lisibilité et la convivialité (les opérateurs > sont l'exemple canonique) est également une bonne raison.

En ce qui concerne les exemples d'amélioration de l'encapsulation, les classes spécifiquement conçues pour fonctionner avec les internes d'autres classes (les classes de test viennent à l'esprit) sont de bons candidats.

4
répondu Gorpik 2009-01-16 08:33:42

Le créateur de C++ dit que ce n'est pas un principe d'encapsulation, et je le citerai:

Est-ce que "ami" viole l'encapsulation? Aucun. Il ne le fait pas. "Ami" est un mécanisme explicite pour accorder l'accès, tout comme l'adhésion. Vous ne pouvez pas (dans un programme conforme standard) vous accorder l'accès à une classe sans modifier sa source.

Est plus que clair...

4
répondu garzanti 2010-02-11 14:42:53

Nous avons eu un problème intéressant dans une entreprise où j'ai déjà travaillé où nous avons utilisé friend to decent affect. J'ai travaillé dans notre département framework, 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:

         Game
        /    \
 TwoPlayer  SinglePlayer

Toutes ces classes faisaient partie du framework et étaient maintenues par notre équipe. Les jeux produits par la société ont été construits au-dessus de ce cadre dérivant de L'un des jeux pour enfants. Le problème était que le jeu avait des interfaces pour diverses choses auxquelles SinglePlayer et TwoPlayer avaient besoin d'accès mais que nous ne voulions pas exposer en dehors des classes de framework. La solution était de rendre ces interfaces privées et de permettre L'accès à deux joueurs et à un seul joueur via l'amitié.

Honnêtement, toute cette question aurait pu être résolue par une meilleure mise en œuvre de notre système, mais nous étions enfermés dans ce que nous avions.

3
répondu Ray 2008-09-04 23:43:46

Un Autre: ami (+ héritage virtuel) peut être utilisé pour éviter de dériver à partir d'une classe (aka: "faire une classe underivable") => 1, 2

De 2:

 class Fred;

 class FredBase {
 private:
   friend class Fred;
   FredBase() { }
 };

 class Fred : private virtual FredBase {
 public:
   ...
 }; 
3
répondu Gian Paolo Ghilardi 2009-09-07 09:38:07

Pour faire TDD plusieurs fois, j'ai utilisé le mot-clé 'friend' en C++.

Un ami peut-il tout savoir sur moi?


Mise à jour: j'ai trouvé cette réponse précieuse sur le mot-clé "ami" du site Bjarne Stroustrup.

"Friend" est un mécanisme explicite pour accorder l'accès, tout comme l'adhésion.

3
répondu popopome 2010-08-27 21:11:59

En ce qui concerne l'opérateur > il n'y a aucune bonne raison de faire de ces opérateurs des amis. Il est vrai qu'ils ne devraient pas être des fonctions membres, mais ils n'ont pas besoin d'être amis non plus.

La meilleure chose à faire est de créer des fonctions d'impression publique (ostream&) et de lecture (istream&). Ensuite, écrivez l'opérateur > en termes de ces fonctions. Cela donne l'avantage supplémentaire de vous permettre de rendre ces fonctions virtuelles, ce qui fournit une sérialisation virtuelle.

2
répondu 2008-08-21 13:03:12

J'utilise uniquement le mot-clé friend pour unittest protected functions. Certains diront que vous ne devriez pas tester la fonctionnalité protégée. Cependant, je trouve 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, mais j'utilise un template-hack astucieux pour réaliser ceci:

template<typename T>
class FriendIdentity {
public:
  typedef T me;
};

/**
 * A class to get access to protected stuff in unittests. Don't use
 * directly, use friendMe() instead.
 */
template<class ToFriend, typename ParentClass>
class Friender: public ParentClass
{
public:
  Friender() {}
  virtual ~Friender() {}
private:
// MSVC != GCC
#ifdef _MSC_VER
  friend ToFriend;
#else
  friend class FriendIdentity<ToFriend>::me;
#endif
};

/**
 * Gives access to protected variables/functions in unittests.
 * Usage: <code>friendMe(this, someprotectedobject).someProtectedMethod();</code>
 */
template<typename Tester, typename ParentClass>
Friender<Tester, ParentClass> & 
friendMe(Tester * me, ParentClass & instance)
{
    return (Friender<Tester, ParentClass> &)(instance);
}

Cela me permet de faire ce qui suit:

friendMe(this, someClassInstance).someProtectedFunction();

Fonctionne sur GCC et MSVC au moins.

2
répondu larsmoa 2009-09-07 09:03:21

Vous devez être très prudent sur quand/où vous utilisez le mot clé friend, et, comme vous, je l'ai utilisé très rarement. Voici quelques notes sur l'utilisation de friend et les alternatives.

Disons que vous voulez comparer deux objets pour voir s'ils sont égaux. Vous pouvez soit:

  • utilisez des méthodes d'accesseur pour faire la comparaison (vérifiez chaque ivar et déterminez l'égalité).
  • ou, vous pouvez accéder directement à tous les membres en les rendant publics.

Le problème avec la première option, c'est que cela pourrait être beaucoup d'accesseurs, ce qui est (légèrement) plus lent que l'accès direct aux variables, plus difficile à lire et encombrant. Le problème avec la deuxième approche est que vous cassez complètement l'encapsulation.

Ce qui serait bien, c'est si nous pouvions définir une fonction externe qui pourrait encore avoir accès aux membres privés d'une classe. Nous pouvons le faire avec le mot clé friend:

class Beer {
public:
    friend bool equal(Beer a, Beer b);
private:
    // ...
};

La méthode equal(Beer, Beer) a maintenant un accès direct aux a et b les membres privés (qui peuvent être char *brand, float percentAlcohol, etc. C'est un exemple plutôt artificiel, vous appliqueriez plus tôt friend à un == operator surchargé, mais nous y arriverons.

Quelques choses à noter:

Je n'utilise vraiment friends que lorsqu'il est beaucoup plus difficile de le faire dans l'autre sens. Comme autre exemple, de nombreuses fonctions mathématiques vectorielles sont souvent créé comme friends en raison de l'interopérabilité des Mat2x2, Mat3x3, Mat4x4, Vec2, Vec3, Vec4, etc. Et c'est tellement plus facile d'être amis, plutôt que d'avoir à utiliser des accesseurs de partout. Comme l'a souligné, friend est souvent utile lorsqu'il est appliqué à la << (très pratique pour le débogage), >> et peut-être le == opérateur, mais peut également être utilisé pour quelque chose comme ceci:

class Birds {
public:
    friend Birds operator +(Birds, Birds);
private:
    int numberInFlock;
};


Birds operator +(Birds b1, Birds b2) {
    Birds temp;
    temp.numberInFlock = b1.numberInFlock + b2.numberInFlock;
    return temp;
}

Comme je le dis, je n'utilise pas friend très souvent, mais de temps en temps, c'est exactement ce dont vous avez besoin. Espérons que cela aide!

2
répondu Ephemera 2012-12-29 02:55:28

L'exemple d'arbre est un très bon exemple : Avoir un objet implémenté dans quelques classes différentes sans avoir une relation d'héritage.

Peut-être que vous pourriez aussi en avoir besoin pour avoir un constructeur protégé et force les gens à utiliser votre usine "ami".

... OK, Eh bien franchement, vous pouvez vivre sans elle.

1
répondu fulmicoton 2008-08-20 05:51:21

Pour faire TDD plusieurs fois, j'ai utilisé le mot-clé 'friend' en C++.
un ami peut-il tout savoir sur moi?

Non, c'est seulement une amitié à Sens Unique: `(

1
répondu roo 2008-08-20 09:19:44

Une instance spécifique où j'utilise friend est lors de la création de Singleton classes. Le mot-clé friend me permet de créer une fonction d'accesseur, qui est plus concise que d'avoir toujours une méthode "GetInstance()" sur la classe.

/////////////////////////
// Header file
class MySingleton
{
private:
    // Private c-tor for Singleton pattern
    MySingleton() {}

    friend MySingleton& GetMySingleton();
}

// Accessor function - less verbose than having a "GetInstance()"
//   static function on the class
MySingleton& GetMySingleton();


/////////////////////////
// Implementation file
MySingleton& GetMySingleton()
{
    static MySingleton theInstance;
    return theInstance;
}
1
répondu Matt Dillard 2008-08-20 13:40:41

Les fonctions et les classes D'amis fournissent un accès direct aux membres privés et protégés de la classe pour éviter de casser l'encapsulation dans le cas général. La plupart des utilisations sont avec ostream: nous aimerions pouvoir taper:

Point p;
cout << p;

Cependant, cela peut nécessiter l'accès aux données privées du Point, nous définissons donc l'opérateur surchargé

friend ostream& operator<<(ostream& output, const Point& p);

Il y a cependant des implications évidentes d'encapsulation. Tout d'abord, maintenant la classe ou la fonction ami a un accès complet à tous les membres de la classe, même ceux qui ne se rapportent pas à ses besoins. Deuxièmement, les implémentations de la classe et de l'ami sont maintenant empêtrées au point où un changement interne dans la classe peut briser l'ami.

Si vous affichez l'ami comme une extension de la classe, alors ce n'est pas un problème, logiquement parlant. Mais, dans ce cas, pourquoi était-il nécessaire de lancer l'ami en premier lieu.

Pour réaliser la même chose que les "amis" prétendent réaliser, mais sans casser l'encapsulation, on peut faites ceci:

class A
{
public:
    void need_your_data(B & myBuddy)
    {
        myBuddy.take_this_name(name_);
    }
private:
    string name_;
};

class B
{
public:
    void print_buddy_name(A & myBuddy)
    {
        myBuddy.need_your_data(*this);
    }
    void take_this_name(const string & name)
    {
        cout << name;
    }
}; 

L'Encapsulation n'est pas cassée, 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 un ami de A. Le compilateur optimisera les appels de fonction, ce qui entraînera les mêmes instructions que direct access.

Je pense que l'utilisation de 'friend' est simplement un raccourci avec un avantage discutable, mais un coût défini.

1
répondu Lubo Antonov 2012-03-28 14:08:56

En C++, le mot-clé "ami" est utile pour surcharger L'opérateur et créer un pont.

1.) Mot-clé ami dans la surcharge de l'opérateur:
exemple pour la surcharge de l'opérateur est: disons que nous avons une classe "Point" qui a deux variables float
" x " (Pour x-coordinate) et "y" (pour y-coordinate). Maintenant, nous devons surcharger "<<" (opérateur d'extraction) de sorte que si nous appelons "cout << pointobj" alors il imprimera les coordonnées x et y (où pointobj est un objet de point de classe). Pour ce faire nous avons deux option:

   1.Overload "operator <<()" function in "ostream" class.
   2.Overload "operator<<()" function in "Point" class.
Maintenant, la première option n'est pas bonne car si nous devons surcharger à nouveau cet opérateur pour une classe différente, nous devons à nouveau changer la classe "ostream".
C'est pourquoi la deuxième est la meilleure option. Maintenant le compilateur peut appeler "operator <<()" fonction:
   1.Using ostream object cout.As: cout.operator&lt&lt(Pointobj) (form ostream class).
2.Call without an object.As: operator&lt&lt(cout, Pointobj) (from Point class).

Beacause nous avons implémenté la surcharge dans la classe de Point. Donc, pour appeler cette fonction Sans objet, nous devons ajouter "friend" mot-clé car nous pouvons appeler une fonction d'ami sans objet. Maintenant la déclaration de fonction sera Comme:
"friend ostream &operator<<(ostream &cout, Point &pointobj);"

2.) Mot-clé ami dans la fabrication de pont:
Supposons que nous devons faire une fonction dans laquelle nous devons accéder à un membre privé de deux classes ou plus ( généralement appelé "pont" ) . Comment faire ceci:
Accès privé membre 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 en tant que fonction d'ami. Exemple : Supposons qu'il y ait deux classes A et B. Une fonction "funcBridge()" veut accéder à private les membres de ces deux classes. Alors les deux classes devraient déclarer "funcBridge()" comme:
friend return_type funcBridge(A &a_obj, B & b_obj);

je pense que cela aiderait à comprendre le mot-clé ami.

1
répondu jadnap 2016-03-25 18:05:27

Comme référence pour déclaration d'ami dit:

La déclaration d'ami apparaît dans un corps de classe et accorde à une fonction ou à une autre classe un accès auxmembres privés et protégés de la classe où la déclaration d'ami apparaît.

Donc, juste pour rappel, il y a des erreurs techniques dans certaines des réponses qui disent que friend seulement protégé membres.

1
répondu lixunhuan 2017-08-18 03:53:02

Lors de l'implémentation d'algorithmes d'arborescence pour la classe, le code d'infrastructure que le prof nous a donné avait la classe tree comme ami de la classe node.

Cela ne fait pas vraiment de bien, à part vous permettre d'accéder à une variable membre sans utiliser de fonction de réglage.

0
répondu Ryan Fox 2008-08-20 05:33:58

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.

Les cas D'utilisation typiques des fonctions friend sont des opérations conduite entre deux classes différentes accédant privé ou protégé les membres des deux.

À Partir de http://www.cplusplus.com/doc/tutorial/inheritance/ .

Vous pouvez voir cet exemple où la méthode non-membre accède aux membres privés de classe. Cette méthode doit être déclarée dans cette très classe comme un ami de la classe.

// friend functions
#include <iostream>
using namespace std;

class Rectangle {
    int width, height;
  public:
    Rectangle() {}
    Rectangle (int x, int y) : width(x), height(y) {}
    int area() {return width * height;}
    friend Rectangle duplicate (const Rectangle&);
};

Rectangle duplicate (const Rectangle& param)
{
  Rectangle res;
  res.width = param.width*2;
  res.height = param.height*2;
  return res;
}

int main () {
  Rectangle foo;
  Rectangle bar (2,3);
  foo = duplicate (bar);
  cout << foo.area() << '\n';
  return 0;
}
0
répondu octoback 2014-02-06 09:50:38

Probablement j'ai manqué quelque chose des réponses ci-dessus mais un autre concept important dans l'encapsulation est le masquage de l'implémentation. Réduire l'accès aux membres de données privées (les détails d'implémentation d'une classe) permet une modification beaucoup plus facile du code plus tard. 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), casser le code accédant à ces données. L'utilisation de méthodes d'accès élimine principalement cela. Assez important je pense.

0
répondu peterdcasey 2016-04-07 20:01:40

Ce n'est peut-être pas une situation de cas d'utilisation réelle mais peut aider à illustrer l'utilisation de friend entre les classes.

Le Club-House

class ClubHouse {
public:
    friend class VIPMember; // VIP Members Have Full Access To Class
private:
    unsigned nonMembers_;
    unsigned paidMembers_;
    unsigned vipMembers;

    std::vector<Member> members_;
public:
    ClubHouse() : nonMembers_(0), paidMembers_(0), vipMembers(0) {}

    addMember( const Member& member ) { // ...code }   
    void updateMembership( unsigned memberID, Member::MembershipType type ) { // ...code }
    Amenity getAmenity( unsigned memberID ) { // ...code }

protected:
    void joinVIPEvent( unsigned memberID ) { // ...code }

}; // ClubHouse

Les Membres de la Classe

class Member {
public:
    enum MemberShipType {
        NON_MEMBER_PAID_EVENT,   // Single Event Paid (At Door)
        PAID_MEMBERSHIP,         // Monthly - Yearly Subscription
        VIP_MEMBERSHIP,          // Highest Possible Membership
    }; // MemberShipType

protected:
    MemberShipType type_;
    unsigned id_;
    Amenity amenity_;
public:
    Member( unsigned id, MemberShipType type ) : id_(id), type_(type) {}
    virtual ~Member(){}
    unsigned getId() const { return id_; }
    MemberShipType getType() const { return type_; }
    virtual void getAmenityFromClubHouse() = 0       
};

class NonMember : public Member {
public:
   explicit NonMember( unsigned id ) : Member( id, MemberShipType::NON_MEMBER_PAID_EVENT ) {}   

   void getAmenityFromClubHouse() override {
       Amenity = ClubHouse::getAmenity( this->id_ );
    }
};

class PaidMember : public Member {
public:
    explicit PaidMember( unsigned id ) : Member( id, MemberShipType::PAID_MEMBERSHIP ) {}

    void getAmenityFromClubHouse() override {
       Amenity = ClubHouse::getAmenity( this->id_ );
    }
};

class VIPMember : public Member {
public:
    friend class ClubHouse;
public:
    explicit VIPMember( unsigned id ) : Member( id, MemberShipType::VIP_MEMBERSHIP ) {}

    void getAmenityFromClubHouse() override {
       Amenity = ClubHouse::getAmenity( this->id_ );
    }

    void attendVIPEvent() {
        ClubHouse::joinVIPEvent( this->id );
    }
};

Équipements

class Amenity{};

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 énuméré les types communs et les classes externes peuvent accéder à leurs ID et Types via les fonctions d'accès qui se trouvent dans la classe de base.

Cependant, à travers ce genre de hiérarchie des membres et de ses classes dérivées et leur relation avec la classe ClubHouse, la seule de la 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 () du ClubHouse, mais la classe membre VIP a cela privilège comme s'il avait un accès complet à cet événement.

Donc, avec le VIPMember et le ClubHouse, c'est une rue d'accès à deux voies où les autres Classes de membres sont limitées.

0
répondu Francis Cugler 2017-02-12 11:43:44

Vous pourriez adhérer aux principes de POO les plus stricts et les plus purs et vous assurer qu'aucun membre de données pour une classe n'a mêmeaccesseurs de sorte que tous les objetsdoivent être les seuls qui peuvent connaître leurs données avec la seule façon d'agir sur eux est

Mais même C # a un mot cléInterne visibility et Java a son niveau d'accessibilité par défaut package pour certaines choses. C++ se rapproche réellement à l'idéal POO par minimizinbg le compromis de visibilité dans une classe en spécifiant exactement quelle autre classe et seulement autres classes pourraient y voir.

Je n'utilise pas vraiment C++ mais si C# avait friendS je le ferais au lieu du modificateur assembly-global internal, que j'utilise beaucoup. Cela ne casse pas vraiment l'incapsulation, car l'Unité de déploiement dans. NET est un assembly.

Mais alors il y a le InternalsVisibleTo attribut (otherAssembly) qui agit comme un mécanisme d'assemblage croisé friend. Microsoft l'utilise pour les assemblages visual designer.

-1
répondu Mark Cidade 2008-08-20 07:46:14

Les Amis sont également utiles pour les rappels. Vous pouvez implémenter des callbacks en tant que méthodes statiques

class MyFoo
{
private:
    static void callback(void * data, void * clientData);
    void localCallback();
    ...
};

callback appelle localCallback en interne, et le clientData contient votre instance. À mon avis,

Ou...

class MyFoo
{
    friend void callback(void * data, void * callData);
    void localCallback();
}

Ce que cela permet, c'est que l'ami soit défini purement dans le cpp en tant que fonction de style c, et ne pas encombrer la classe.

De Même, un modèle que j'ai vu très souvent est de mettre toutes les vraiment membres privés d'une classe dans une autre classe, qui est déclaré dans l'en-tête, défini 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:

class MyFooPrivate;
class MyFoo
{
    friend class MyFooPrivate;
public:
    MyFoo();
    // Public stuff
private:
    MyFooPrivate _private;
    // Other private members as needed
};

Dans le rpc,

class MyFooPrivate
{
public:
   MyFoo *owner;
   // Your complexity here
};

MyFoo::MyFoo()
{
    this->_private->owner = this;
}

Il devient plus facile de cacher des choses que l'aval n'a pas besoin de voir de cette façon.

-1
répondu shash 2008-12-12 12:10:05