webentwicklung-frage-antwort-db.com.de

Warum lässt C++ keine ererbte Freundschaft zu?

Warum ist Freundschaft in C++ nicht optional optional vererbbar? Ich verstehe, dass Transitivität und Reflexivität aus naheliegenden Gründen verboten ist (ich sage das nur, um einfache FAQ Zitierantworten abzuwehren), aber das Fehlen von etwas in der Art von virtual friend class Foo; verwirrt mich. Kennt jemand den historischen Hintergrund dieser Entscheidung? War Freundschaft wirklich nur ein begrenzter Hack, der seitdem in ein paar obskure, respektable Nutzungen Einzug gehalten hat?

Zur Klarstellung bearbeiten: Ich spreche über das folgende Szenario nicht, in dem Kinder von A entweder B oder B und dessen Kindern ausgesetzt sind. Ich kann mir auch vorstellen, optional Zugriff auf Überschreibungen von Friend-Funktionen usw. zu gewähren.

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. */ };

Angenommene Antwort: wie Loki sagt , der Effekt kann mehr oder weniger simuliert werden, indem geschützte Proxyfunktionen in befreundeten Basisklassen erstellt werden, so dass es keine strikte need für die Gewährung von Freundschaft zu einem gibt Klassen- oder virtuelle Methodenhirarchie. Ich mag die Notwendigkeit von Boilerplate-Proxies nicht (was die befreundete Basis tatsächlich wird), aber ich nehme an, dass dies einem Sprachmechanismus vorgezogen wurde, der am ehesten missbraucht werden könnte. Ich denke, es ist wahrscheinlich an der Zeit, dass ich Stroupstrup The Design and Evolution of C++ gekauft und gelesen habe. Ich habe hier schon genug Leute gesehen, die empfohlen wurden, um einen besseren Einblick in diese Art von Fragen zu erhalten ...

83
Jeff

Weil ich Foo und seinen Freund Bar schreiben kann (also gibt es eine Vertrauensbeziehung). 

Aber vertraue ich den Leuten, die Klassen schreiben, die von Bar abgeleitet sind?
Nicht wirklich. Sie sollten also keine Freundschaft erben.

Jede Änderung in der internen Darstellung einer Klasse erfordert eine Änderung an allem, was von dieser Darstellung abhängt. Daher müssen alle Mitglieder einer Klasse und auch alle Freunde der Klasse geändert werden. 

Wenn also die interne Darstellung von Foo geändert wird, muss auch Bar geändert werden (da die Freundschaft Bar eng an Foo bindet). Wenn die Freundschaft vererbt wurde, wären alle von Bar abgeleiteten Klassen eng an Foo gebunden und müssen daher geändert werden, wenn die interne Darstellung von Foo geändert wird. Ich habe jedoch keine Kenntnis von abgeleiteten Typen (und ich sollte auch nicht. Sie können sogar von verschiedenen Unternehmen entwickelt werden). Daher wäre ich nicht in der Lage, Foo zu ändern, da dies die Code-Basis grundlegend ändern würde (da ich nicht alle von Bar abgeleiteten Klassen ändern konnte).

Wenn also Freundschaft vererbt wurde, führen Sie unbeabsichtigt eine Einschränkung für die Möglichkeit ein, eine Klasse zu ändern. Dies ist unerwünscht, da Sie das Konzept einer öffentlichen API grundsätzlich unbrauchbar machen.

Hinweis: Ein untergeordnetes Element von Bar kann mithilfe von Foo auf Bar zugreifen. Die Methode in Bar muss jedoch nur geschützt werden. Dann kann das untergeordnete Element von Bar auf eine Foo zugreifen, indem es durch seine übergeordnete Klasse aufgerufen wird.

Ist das was du willst?

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);
        }
};
79
Martin York

Warum ist Freundschaft in C++ nicht optional optional vererbbar?

Ich denke, die Antwort auf Ihre erste Frage lautet: "Haben die Freunde Ihres Vaters Zugang zu Ihrem Privat?

40
wilx

Eine befreundete Klasse kann ihren Freund über Accessor-Funktionen verfügbar machen und dann den Zugriff durch diese gewähren.

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(); }
};

Dies ermöglicht eine feinere Steuerung als die optionale Durchlässigkeit. Beispielsweise kann get_cashprotected sein oder ein Protokoll mit laufzeitbeschränktem Zugriff erzwingen.

8
Potatoswatter

C++ - Standard, Abschnitt 11.4/8

Freundschaft ist weder vererbt noch transitiv.

Wenn die Freundschaft vererbt würde, hätte eine Klasse, die nicht als Freund gedacht war, plötzlich Zugang zu den Klasseninhalten der Klasse und verstößt gegen die Kapselung.

7
David

Weil es einfach nicht nötig ist.

Die Verwendung des Schlüsselworts friend ist selbst verdächtig. In Bezug auf die Kopplung ist dies die schlechteste Beziehung (weit vor Vererbung und Zusammensetzung).

Jede Änderung an den Internen einer Klasse hat das Risiko, die Freunde dieser Klasse zu beeinflussen. Wollen Sie wirklich eine unbekannte Anzahl von Freunden? Sie wären nicht einmal in der Lage, sie aufzulisten, wenn die, die von ihnen erben, auch Freunde sein könnten, und Sie würden das Risiko eingehen, den Code Ihres Kunden jedes Mal zu brechen. Dies ist sicherlich nicht wünschenswert.

Ich gebe gerne zu, dass die Abhängigkeit von Hausaufgaben/Heimtierprojekten oft weit entfernt ist. Bei kleinen Projekten spielt es keine Rolle. Sobald jedoch mehrere Personen an demselben Projekt arbeiten und dies zu Dutzenden von Zeilen führt, müssen Sie die Auswirkungen von Änderungen einschränken.

Dies bringt eine sehr einfache Regel:

Das Ändern der Interna einer Klasse sollte nur die Klasse selbst betreffen

Natürlich beeinflussen Sie wahrscheinlich seine Freunde, aber hier gibt es zwei Fälle:

  • friend-free-funktion: wahrscheinlich ohnehin eher eine member-funktion (ich denke, std::ostream& operator<<(...) hier, das kein reines Mitglied ist, rein durch zufall der sprachregeln
  • freundenklasse? Sie brauchen keine Freundesklassen für echte Klassen.

Ich würde die Verwendung der einfachen Methode empfehlen:

class Example;

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

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

Mit diesem einfachen Key-Muster können Sie einen Freund (in gewisser Weise) deklarieren, ohne ihm tatsächlich Zugriff auf Ihre Interna zu gewähren und ihn so von Änderungen zu isolieren. Außerdem kann dieser Freund bei Bedarf seinen Treuhänder (wie Kinder) den Schlüssel verleihen.

2
Matthieu M.

Einfache Logik: 'Ich habe eine Freundin Jane. Nur weil wir gestern Freunde geworden sind, sind nicht alle ihre Freunde zu meinen. ' 

Ich muss immer noch diese individuellen Freundschaften genehmigen, und das Vertrauen wäre entsprechend.

0
Robert Hamm

Die Friend-Funktion in einer Klasse weist der Funktion die externe Eigenschaft zu. Extern bedeutet, dass die Funktion irgendwo außerhalb der Klasse deklariert und definiert wurde. 

Das bedeutet, dass die Freundesfunktion kein Mitglied einer Klasse ist. Mit der Vererbung können Sie also nur die Eigenschaften einer Klasse und nicht externe Dinge übernehmen. Und auch wenn Vererbung für Freundesfunktionen erlaubt ist, erbt eine Fremdklasse. 

0
abdul rizwan

Eine abgeleitete Klasse kann nur etwas erben, das Mitglied der Basis ist. Eine Freundschaftserklärung istnichtein Mitglied der befreundeten Klasse.

$ 11.4/1- "... Der Name eines Freundes ist Nicht im Gültigkeitsbereich der Klasse, und der Freund wird nicht mit den Zugriffsoperatoren (5.2.5) des Members aufgerufen, sofern dies nicht der Fall ist ein Mitglied einer anderen Klasse. "

$ 11.4 - "Da die Basisklausel Der Friend-Klasse nicht Teil ihrer Member-Deklarationen ist, kann die Basisklausel Der Friend-Klasse nicht auf die -Namen von private und geschützt Mitglieder aus der Klasse, die Freundschaft gewährt. "

und weiter

$ 10.3/7- "[Hinweis: Der virtuelle Bezeichner Impliziert die Mitgliedschaft. Eine virtuelle -Funktion kann keine Nicht-Member-Funktion (7.1.2) Sein. Eine virtuelle Funktion Kann auch kein statisches Member sein , da ein virtueller Funktionsaufruf auf ein bestimmtes Objekt bezogen ist, um festzustellen, welche Funktion aufgerufen werden soll. Eine virtuelle Funktion, die in einer Klasse deklariert ist, kann in einer anderen Klasse als Freund deklariert werden.] "

Da der "Freund" überhaupt nicht Mitglied der Basisklasse ist, wie kann er von der abgeleiteten Klasse geerbt werden?

0
Chubsdad

Friend ist gut in der Vererbung, wie die Stilschnittstelle für Container Aber für mich fehlt C++ die propagierbare Vererbung

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
    }
};

Für mich ist das Problem von C++ das Fehlen einer sehr guten Granularität, um den Zugriff von überall auf alles zu kontrollieren:

freund Thing kann Freund Thing werden. *, um allen Kindern von Thing Zugriff zu gewähren 

Und mehr, friend [named area] Thing. *, Um Zugriff auf ein präzises Zu gewähren, befindet sich in der Container-Klasse über einen speziellen, benannten Bereich für den Friend.

Ok, hör auf den Traum. Aber jetzt kennen Sie eine interessante Verwendung von Freunden.

In einer anderen Reihenfolge kann man auch interessant finden, dass alle Klassen mit sich selbst freundlich sind. In anderen Worten kann eine Klasseninstanz alle aufrufen
Mitglieder einer anderen Instanz mit demselben Namen ohne Einschränkung:

class Object {
     private:
         void test() {}
     protected:
         void callAnotherTest(Object* anotherObject) {
             //private, but yes you can call test() from 
             //another object instance
             anotherObject)->test(); 
         }
};
0
zep

Eine Vermutung: Wenn eine Klasse eine andere Klasse/Funktion als Freund deklariert, ist dies darauf zurückzuführen, dass diese zweite Entität privilegierten Zugriff auf die erste Entität benötigt. Was nützt es, der zweiten Entität einen privilegierten Zugriff auf eine beliebige Anzahl von Klassen zu gewähren, die von der ersten abgeleitet werden?

0