100 votes

Pourquoi le C++ ne permet-il pas l'amitié héritée ?

Pourquoi l'amitié n'est pas au moins optionnellement héritable en C++ ? Je comprends que la transitivité et la réflexivité soient interdites pour des raisons évidentes (je dis cela uniquement pour éviter les réponses simples de type FAQ), mais l'absence de quelque chose du type virtual friend class Foo; me laisse perplexe. Quelqu'un connaît-il le contexte historique de cette décision ? L'amitié n'était-elle vraiment qu'un hack limité qui a depuis trouvé sa place dans quelques utilisations obscures et respectables ?

Editer pour clarification : Je parle du scénario suivant, no où les enfants de A sont exposés soit à B, soit à B et à ses enfants. Je peux aussi imaginer d'accorder facultativement l'accès aux surcharges des fonctions amies, etc.

class A {
  int x;
  friend class B;
};

class B {
  // OK as per friend declaration above.
  void foo(A& a, int n) { a.x = n; }
};

class D : public B { /* can't get in A w/o 'friend class D' declaration. */ };

Réponse acceptée : comme Loki déclare l'effet peut être plus ou moins simulé en créant des fonctions proxy protégées dans les classes de base amies, de sorte qu'il n'y a pas d'obligation stricte d'utiliser des fonctions proxy. besoin de pour accorder l'amitié à une classe ou à une hiérarchie de méthodes virtuelles. Je n'aime pas le besoin de proxies passe-partout (que la base amie devient effectivement), mais je suppose que cela a été jugé préférable à un mécanisme de langage qui serait plus probablement mal utilisé la plupart du temps. Je pense qu'il est temps que j'achète et lise l'ouvrage de Stroupstrup. La conception et l'évolution du C++ que j'ai vu suffisamment de personnes ici recommander, pour avoir un meilleur aperçu de ce type de questions ...

100voto

Loki Astari Points 116129

Parce que je peux écrire Foo et son ami Bar (il existe donc une relation de confiance).

Mais est-ce que je fais confiance aux personnes qui écrivent des classes qui sont dérivées de Bar ?
Pas vraiment. Donc ils ne devraient pas hériter de l'amitié.

Tout changement dans la représentation interne d'une classe nécessitera une modification de tout ce qui dépend de cette représentation. Ainsi, tous les membres d'une classe et également tous les amis de la classe devront être modifiés.

Par conséquent, si la représentation interne de Foo est modifié, alors Bar doit également être modifié (parce que l'amitié lie étroitement Bar à Foo ). Si l'amitié était héritée, alors toutes les classes dérivées de Bar serait aussi étroitement lié à Foo et nécessitent donc une modification si Foo La représentation interne de l'entreprise est modifiée. Mais je n'ai aucune connaissance des types dérivés (et je ne devrais pas en avoir. Ils peuvent même être développés par des sociétés différentes, etc.) Je serais donc incapable de modifier Foo car cela introduirait des changements brutaux dans le code de base (car je ne peux pas modifier toutes les classes dérivées de Bar ).

Ainsi, si l'amitié a été héritée, vous introduisez par inadvertance une restriction sur la possibilité de modifier une classe. Ceci n'est pas souhaitable car vous rendez inutile le concept d'API publique.

Note : Un enfant de Bar peut accéder Foo en utilisant Bar il suffit de mettre la méthode dans Bar protégé. Alors l'enfant de Bar peut accéder à un Foo en faisant appel à sa classe mère.

C'est ce que vous voulez ?

class A
{
    int x;
    friend class B;
};

class B
{
    protected:
       // Now children of B can access foo
       void foo(A& a, int n) { a.x = n; }
};

class D : public B
{
    public:
        foo(A& a, int n)
        {
            B::foo(a, n + 5);
        }
};

4 votes

Bingo. Il s'agit de limiter les dégâts que l'on peut causer en modifiant les internes d'une classe.

0 votes

Franchement, le cas auquel je pense vraiment est le pattern Avocat-Client, où un intermédiaire agit comme une interface limitée aux classes externes en présentant des méthodes enveloppantes à la classe sous-jacente à accès restreint. Dire qu'une interface est disponible pour tous les enfants d'autres classes plutôt qu'une classe exacte serait beaucoup plus utile que le système actuel.

0 votes

@Jeff : Exposer la représentation interne à tous les enfants d'une classe rendra le code inchangeable (cela brise également l'encapsulation car toute personne souhaitant accéder aux membres internes doit hériter de Bar même si elle n'est pas vraiment un Bar).

52voto

Václav Zeman Points 7911

Pourquoi l'amitié n'est-elle pas au moins facultativement héritable en C++ ?

Je pense que la réponse à votre première question se trouve dans cette question : "Les amis de ton père ont-ils accès à tes parties intimes ?"

42 votes

Pour être juste, cette question en soulève de troublantes au sujet de votre père. . .

3 votes

Quel est l'intérêt de cette réponse ? Au mieux, un commentaire douteux, bien que peut-être léger.

10voto

Potatoswatter Points 70305

Une classe amie peut exposer son ami par le biais de fonctions d'accès, puis lui accorder l'accès par le biais de celles-ci.

class stingy {
    int pennies;
    friend class hot_girl;
};

class hot_girl {
public:
    stingy *bf;

    int &get_cash( stingy &x = *bf ) { return x.pennies; }
};

class moocher {
public: // moocher can access stingy's pennies despite not being a friend
    int &get_cash( hot_girl &x ) { return x.get_cash(); }
};

Cela permet un contrôle plus fin que la transitivité optionnelle. Par exemple, get_cash peut être protected ou peut appliquer un protocole d'accès limité dans le temps.

0 votes

@Hector : Votez pour le remaniement ! ;)

10voto

David Points 1773

Norme C++, section 11.4/8

L'amitié n'est ni héritée ni transitive.

Si l'amitié était héritée, alors une classe qui n'était pas censée être une amie aurait soudainement accès aux internes de votre classe et cela viole l'encapsulation.

2 votes

Disons que Q est l'"ami" de A, et que B est dérivé de A. Si B hérite de l'amitié de A, alors comme B est un type de A, techniquement c'est un A qui a accès aux données privées de Q. Cela ne répond donc pas à la question avec une raison pratique.

2voto

Matthieu M. Points 101624

Parce que c'est juste inutile.

L'utilisation de la friend mot-clé est lui-même suspect. En termes de couplage, c'est la pire relation (loin devant l'héritage et la composition).

Toute modification des éléments internes d'une classe risque d'avoir un impact sur les amis de cette classe... voulez-vous vraiment un nombre inconnu d'amis ? Vous ne seriez même pas capable de les lister si ceux qui héritent d'eux pouvaient aussi être des amis, et vous courriez le risque de casser le code de vos clients à chaque fois, ce n'est sûrement pas souhaitable.

J'admets volontiers que pour les devoirs/projets d'animaux domestiques, la dépendance est souvent une considération lointaine. Pour les projets de petite taille, cela n'a pas d'importance. Mais dès que plusieurs personnes travaillent sur le même projet et que celui-ci atteint des dizaines de milliers de lignes, il faut limiter l'impact des changements.

Cela apporte une règle très simple :

La modification des éléments internes d'une classe ne doit affecter que la classe elle-même.

Bien sûr, vous affecterez probablement ses amis, mais il y a deux cas de figure :

  • fonction libre amie : probablement plus une fonction membre de toute façon (je pense que std::ostream& operator<<(...) ici, qui n'est pas membre par pur hasard des règles linguistiques
  • classe d'amis ? vous n'avez pas besoin de classes d'amis sur des classes réelles.

Je recommande l'utilisation de la méthode simple :

class Example;

class ExampleKey { friend class Example; ExampleKey(); };

class Restricted
{
public:
  void forExampleOnly(int,int,ExampleKey const&);
};

Ce simple Key vous permet de déclarer un ami (d'une certaine manière) sans lui donner accès à vos internes, l'isolant ainsi des changements. En outre, il permet à cet ami de prêter sa clé à des mandataires (comme des enfants) si nécessaire.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X