403 Stimmen

Wann sollte man 'friend' in C++ verwenden?

Ich habe mich durch die C++ FAQ und war neugierig auf die friend Erklärung. Ich persönlich habe sie noch nie benutzt, bin aber daran interessiert, diese Sprache zu erforschen.

Was ist ein gutes Beispiel für die Verwendung von friend ?


Wenn ich die FAQ ein wenig länger lese, gefällt mir die Idee des << >> Operatorüberladung und Hinzufügen als Freund dieser Klassen. Ich bin mir jedoch nicht sicher, ob dies nicht gegen die Kapselung verstößt. Wann können diese Ausnahmen innerhalb der Strenge des OOP bleiben?

26voto

jalf Punkte 235501

Bearbeiten: Lesen der FAQ ein bisschen länger Ich mag die Idee der << >> Operator Überlastung und Hinzufügen als ein Freund dieser Klassen, aber ich bin nicht sicher, wie dies nicht brechen Kapselung

Inwiefern würde es die Verkapselung aufheben?

Sie brechen die Kapselung, wenn Sie erlauben Unbeschränkt Zugriff auf ein Datenelement. Betrachten Sie die folgenden Klassen:

class c1 {
public:
  int x;
};

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

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

c1 es natürlich nicht gekapselt. Jeder kann lesen und ändern x darin. Wir haben keine Möglichkeit, irgendeine Art von Zugangskontrolle durchzusetzen.

c2 ist offensichtlich gekapselt. Es gibt keinen öffentlichen Zugang zu x . Alles, was Sie tun können, ist den foo Funktion, die Folgendes ausführt eine sinnvolle Operation an der Klasse .

c3 ? Ist das weniger gekapselt? Erlaubt es uneingeschränkten Zugang zu x ? Erlaubt sie unbekannten Funktionen den Zugriff?

Nein. Es erlaubt genau eine Funktion, um auf die privaten Mitglieder der Klasse zuzugreifen. Genau wie c2 hat. Und genau wie c2 ist die einzige Funktion, die Zugriff hat, nicht "irgendeine beliebige, unbekannte Funktion", sondern "die in der Klassendefinition aufgeführte Funktion". Genau wie c2 können wir anhand der Klassendefinitionen erkennen, dass eine vollständig Liste der Personen, die Zugang haben.

Wie genau ist das weniger gekapselt? Die gleiche Menge an Code hat Zugriff auf die privaten Mitglieder der Klasse. Und alle wer Zugang hat, ist in der Klassendefinition aufgeführt.

friend bricht nicht die Verkapselung. Einige Java-Programmierer fühlen sich dabei unwohl, denn wenn sie "OOP" sagen, meinen sie eigentlich mittlere "Java". Wenn sie "Kapselung" sagen, meinen sie nicht "private Mitglieder müssen vor willkürlichen Zugriffen geschützt werden", sondern "eine Java-Klasse, in der die einzigen Funktionen, die auf private Mitglieder zugreifen können, Klassenmitglieder sind", obwohl das völliger Unsinn ist aus mehreren Gründen .

Erstens ist sie, wie bereits dargelegt, zu restriktiv. Es gibt keinen Grund, warum die Methoden von Freunden nicht dasselbe tun sollten.

Zweitens ist sie nicht restriktiv genug . Betrachten Sie eine vierte Klasse:

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

Dies ist, gemäß der oben erwähnten Java-Mentalität, perfekt gekapselt. Und doch erlaubt es absolut jedem, x . Wie kann das überhaupt einen Sinn ergeben? (Hinweis: Tut es nicht)

Unterm Strich: Bei der Kapselung geht es darum, dass man kontrollieren kann, welche Funktionen auf private Mitglieder zugreifen können. Sie ist nicht darüber, wo genau die Definitionen dieser Funktionen zu finden sind.

10voto

maccullt Punkte 2719

Eine andere gängige Version von Andrews Beispiel, die gefürchtete Code-Kopplung

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

Anstatt sich Gedanken darüber zu machen, ob beide Zeilen immer zusammen und in der gleichen Reihenfolge ausgeführt werden, könnten Sie die Methoden privat machen und eine Freundschaftsfunktion verwenden, um die Konsistenz zu erzwingen:

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

Mit anderen Worten: Sie können die öffentlichen Schnittstellen kleiner halten und Invarianten, die klassen- und objektübergreifend sind, in Freundschaftsfunktionen erzwingen.

8voto

VladimirS Punkte 590

Ich habe einen praktischen Ort gefunden, um den Freundeszugang zu nutzen: Unittest von privaten Funktionen.

8voto

csmba Punkte 4033

Sie steuern die Zugriffsrechte für Mitglieder und Funktionen über Private/Protected/Public right? Wenn man also davon ausgeht, dass die Idee jeder einzelnen dieser 3 Ebenen klar ist, dann sollte es klar sein, dass wir etwas übersehen...

Die Deklaration eines Mitglieds/einer Funktion als geschützt ist zum Beispiel sehr allgemein gehalten. Sie sagen also, dass diese Funktion unerreichbar ist für alle (außer natürlich bei einem geerbten Kind). Aber was ist mit Ausnahmen? Bei jedem Sicherheitssystem gibt es doch eine Art "weiße Liste", oder?

Also Freund bietet die Flexibilität einer felsenfesten Isolierung von Objekten, ermöglicht aber auch ein "Schlupfloch" für Dinge, die Sie für gerechtfertigt halten.

Ich vermute, dass die Leute sagen, es sei nicht nötig, weil es immer ein Design gibt, das ohne sie auskommt. Ich denke, es ist ähnlich wie bei der Diskussion über globale Variablen: Man sollte sie nie verwenden, es gibt immer einen Weg, ohne sie auszukommen... aber in der Realität sieht man Fälle, in denen das am Ende der (fast) eleganteste Weg ist... Ich denke, das ist auch bei Freunden der Fall.

Es bringt nicht wirklich etwas, außer dass Sie auf eine Mitgliedsvariable zugreifen können, ohne eine Einstellungsfunktion zu verwenden

Nun, das ist nicht gerade die richtige Betrachtungsweise. Die Idee ist, zu kontrollieren, WER auf was zugreifen kann, ob er eine Einstellungsfunktion hat wenig damit zu tun.

6voto

garzanti Punkte 2102

Der Schöpfer von C++ sagt, dass dies nicht gegen das Prinzip der Kapselung verstößt, und ich werde ihn zitieren:

Verstößt "Freund" gegen die Kapselung? Nein, das tut es nicht. "Freund" ist ein expliziter Mechanismus zur Gewährung von Zugang, genau wie die Mitgliedschaft. Sie können sich (in einem standardkonformen Programm) nicht selbst Zugriff auf eine Klasse gewähren, ohne ihren Quelltext zu ändern.

Ist mehr als klar...

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X