Pourquoi c++ ne permet pas l'amitié héréditaire?

pourquoi l'amitié n'est-elle pas au moins optionnellement héréditaire en C++? Je comprends que la transitivité et la réflexivité soient interdites pour des raisons évidentes (je dis cela seulement pour éviter les réponses simples aux questions fréquemment posées), mais le manque de quelque chose dans le sens de virtual friend class Foo; me laisse perplexe. Personne ne sait le contexte historique derrière cette décision? Est-ce que l'amitié était vraiment juste un piratage limité qui a depuis trouvé son chemin dans quelques obscures utilisations respectables?

Éditer pour la clarification: je parle du scénario suivant, pas où les enfants de A sont exposés à B ou à B et ses enfants. Je peux aussi imaginer accorder l'accès optionnel à des dérogations aux fonctions d'ami,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. */ };

Accepted answer: as Loki states , l'effet peut être simulé plus ou moins en faisant des fonctions de proxy protégées dans la base friended classes, il n'y a donc pas de stricte besoin pour accorder l'amitié à une classe ou méthode virtuelle de l'hérirarchie. Je n'aime pas la nécessité de recourir à des approximations de type boilerplate (ce qui fait de la base jumelée une réalité), mais je suppose que cela a été jugé préférable par rapport à un mécanisme de langage qui serait plus susceptible d'être mal utilisé la plupart du temps. Je pense qu'il est probablement temps que j'achète et que je lise Stroupstrup la conception et L'évolution de C++ , que j'ai vu assez de gens ici recommander, pour obtenir un meilleur aperçu de ces types de questions ...

79
demandé sur Jeff 2010-08-25 02:55:01

9 réponses

parce que je peux écrire Foo et son ami Bar (donc il y a une relation de confiance).

mais est-ce que je fais confiance aux gens qui écrivent des classes qui sont dérivées de Bar ?

Pas vraiment. Ils ne doivent donc pas hériter de l'amitié.

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

par conséquent, si la représentation interne de Foo est modifiée, alors Bar doit aussi être modifié (parce que l'amitié lie étroitement Bar à Foo ). Si l'amitié a été héritée alors toute classe dérivée de Bar serait également étroitement liée à Foo et nécessitent donc une modification si Foo la représentation interne est changée. Mais je n'ai aucune connaissance de types dérivés (et I. Ils peuvent même être développés par différentes entreprises, etc.). Je ne pourrais donc pas changer Foo car cela introduirait des changements de rupture dans la base de code (car je ne pourrais 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 capacité de modifier une classe. Ceci n'est pas souhaitable car vous rendez fondamentalement inutile le concept D'une API publique.

Note: un enfant de Bar peut accéder à Foo en utilisant Bar , il suffit de faire la méthode dans Bar protégé. Puis l'enfant de Bar peut accéder à un Foo en appelant à travers sa classe de parent.

C'est ça que tu veux?

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);
        }
};
76
répondu Loki Astari 2017-05-29 03:52:18

pourquoi l'amitié n'est-elle pas au moins optionnellement héréditaire en C++?

je pense que la réponse à votre première question Est dans cette question: " est-ce que les amis de votre père ont accès à vos parties intimes?"

35
répondu wilx 2014-06-19 10:33:50

une classe amie peut exposer son ami à travers les fonctions d'accesseur, puis accorder l'accès à travers ceux-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é à l'exécution.

7
répondu Potatoswatter 2010-08-25 04:46:39

C++ Standard, section 11.4 /8

l'Amitié n'est ni héréditaire, ni transitive.

si l'amitié était héréditaire, alors une classe qui n'était pas censée être une amie aurait soudainement accès à votre intérieur de classe et qui viole l'encapsulation.

7
répondu David 2017-05-29 03:49:59

parce que c'est inutile.

l'utilisation du mot-clé friend est elle-même suspecte. En terme de couplage, c'est la pire relation (loin devant l'héritage et la composition).

Tout changement à l'intérieur d'une classe ont un risque d'impact les amis de cette classe... voulez-vous vraiment un nombre inconnu d'amis ? Vous ne seriez même pas en mesure de les énumérer si ceux qui héritent d'eux peuvent aussi être des amis, et vous courrait dans le risque de briser le code de vos clients à chaque fois, sûrement ce n'est pas souhaitable.

j'admets librement que pour les devoirs/projets de compagnie la dépendance est souvent une considération lointaine. Sur les petits projets, ça n'a pas d'importance. Mais dès que plusieurs personnes travaillent sur le même projet et de ce qui pousse dans les dizaines de milliers de lignes, vous devez limiter l'impact des modifications.

cela apporte une règle très simple:

Modifier les internes d'une classe ne devrait affecter la classe elle-même

bien sûr, vous affecterez probablement ses amis, mais il y a deux cas ici:

  • fonction libre d'ami: probablement plus d'une fonction de membre de toute façon (je pense std::ostream& operator<<(...) ici, qui n'est pas un membre purement par accident des règles de langue
  • classe des amis ? tu n'as pas besoin de cours sur les vrais cours.

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 modèle simple Key vous permet de déclarer un ami (d'une certaine manière) sans lui donner réellement accès à vos internes, l'isolant ainsi des changements. En outre, il permet à cet ami de prêter sa clé aux administrateurs (comme les enfants) si nécessaire.

2
répondu Matthieu M. 2010-08-25 06:36:49

une supposition: si une classe déclare une autre classe/fonction en tant qu'ami, c'est parce que cette deuxième entité a besoin d'un accès privilégié à la première. À quoi sert-il d'accorder à la seconde entité un accès privilégié à un nombre arbitraire de classes dérivées de la première?

0
répondu Oliver Charlesworth 2010-08-24 22:58:13

une classe dérivée ne peut hériter que de quelque chose, qui est "membre" de la base. Une déclaration d'ami est pas un membre de la classe amie.

$11.4/1- "...Le nom d'un ami est pas dans le champ d'application de la classe, et la ami n'est pas appelé avec le membre des opérateurs d'accès (5.2.5) sauf s'il est un membre d'une autre classe."

$ 11.4 - " aussi, parce que le la base de la clause de la classe amie ne fait pas partie de son déclarations des membres, clause de base de la classe ami ne peut pas accéder à noms des personnes privées et protégées membres de la classe accordant amitié."

et plus loin

$10.3/7- "[Note: le spécificateur virtuel implique l'adhésion, de sorte que virtuel fonction ne peut être un tiers (7.1.2) fonction. Ni une fonction virtuelle être un membre statique, depuis un virtuel l'appel de fonction repose sur un objet pour déterminer quelle fonction invoquer. Une fonction virtuelle déclarée dans une classe peut être déclaré un ami dans une autre classe. ] "

Depuis le " ami " n'est pas un membre de la classe de base, en premier lieu, comment peut-il être héritée par la classe dérivée?

0
répondu Chubsdad 2010-08-25 01:59:27

Ami de la fonction dans une classe attribue la extern propriété de la fonction. c'est-à-dire qu'externe signifie que la fonction a été déclarée et définie quelque part en dehors de la classe.

cela signifie donc que la fonction d'ami n'est pas un membre d'une classe. Donc, l'héritage permet d'hériter des propriétés d'une classe pas les choses extérieures. Et aussi si l'héritage est autorisé pour les fonctions d'ami, alors une classe de tiers héritant.

0
répondu Robert 2014-11-26 12:40:18

ami est bon dans l'héritage comme l'interface de style pour conteneur Mais pour moi, comme le premier dire, C++ manque l'héritage propageable

class Thing;

//an interface for Thing container's
struct IThing {
   friend Thing;
   protected:
       int IThing_getData() = 0;
};

//container for thing's
struct MyContainer : public IThing {
    protected: //here is reserved access to Thing
         int IThing_getData() override {...}
};

struct Thing {
    void setYourContainer(IThing* aContainerOfThings) {
        //access to unique function in protected area 
        aContainerOfThings->IThing_getData(); //authorized access
    }
};

struct ChildThing : public Thing {
    void doTest() {
        //here the lack of granularity, you cannot access to the container.
        //to use the container, you must implement all 
        //function in the Thing class
        aContainerOfThings->IThing_getData(); //forbidden access
    }
};

Pour moi, le problème de C++ est le manque de très bonne granularité contrôler tous les accès de n'importe où pour n'importe quoi:

le truc de l'ami peut devenir le truc de l'ami.* accorder l'accès à tout enfant D'une chose

et plus, ami [région nommée] chose.* pour accorder un accès précis sont dans la classe de Conteneur dans le domaine de l'ami.

Ok arrêter le rêve. Mais maintenant, vous connaissez un usage intéressant d'ami.

dans un autre ordre, vous pouvez également trouver intéressant de savoir toutes les classes sont amicaux avec soi-même. En d'autres termes, une instance de classe peut appeler tous

membres d'une autre instance du même nom sans restriction:

class Object {
     private:
         void test() {}
     protected:
         void callAnotherTest(Object* anotherObject) {
             //private, but yes you can call test() from 
             //another object instance
             anotherObject)->test(); 
         }
};
0
répondu zep 2016-12-08 17:06:30