Was ist der Anwendungsfall für die Verwendung der C ++ - Freundklasse?

8

Ich versuche, C ++ Freund zu verstehen. Wann ist der gute Anwendungsfall, um Freund zu verwenden? Ich nehme an, wenn wir einer anderen Klasse Zugriff auf andere Klassenattribute gewähren möchten, warum machen wir es dann nicht einfach als öffentlich oder erben stattdessen von dieser Klasse?

Danke für deine Hilfe.

Joshua Partogi
quelle
4
Ich denke, diese Frage gehört definitiv hierher, da es eher ein "Warum?" Frage als ein "wie?" Frage, und es wurden bereits einige gute Punkte in den Antworten gemacht.
Larry Coleman

Antworten:

14

Um ein Mitglied der Klasse zu werden public, muss jeder Zugriff darauf erhalten, wodurch die Kapselung vollständig unterbrochen wird.

Das Erben einer Klasse ist oft nicht wünschenswert, wenn die Freundesklasse keine Unterklasse sein soll. Das Unterklassen nur, um Zugriff auf die Interna einer Klasse zu erhalten, ist ein schwerwiegender Designfehler. Und selbst eine Unterklasse kann die privaten Mitglieder ihrer Basisklasse nicht sehen.

Eine typische Verwendung von friendist für Operatoren, die keine Mitglieder sein können, wie z. B. Stream-Operatoren operator+usw. In diesen Fällen ist die tatsächliche Klasse, der sie zugeordnet sind, nicht (immer) der erste Parameter der Funktion, daher kann die Funktion dies nicht als Mitgliedsmethode implementiert werden.

Ein weiteres Beispiel ist die Implementierung eines Iterators für eine Sammlung. Der Iterator (und nur der Iterator) muss die Interna seiner übergeordneten Sammlung sehen, ist jedoch keine Unterklasse der Sammlung.

Péter Török
quelle
Manchmal werden diese kostenlosen Freundfunktionen in der Klasse inline definiert.
Deduplikator
3

Das Beispiel, für das ich in C ++ am häufigsten Freundklassen gesehen habe, ist das Testen von Einheiten. Unit-Tests möchten normalerweise alles über Ihre Interna wissen, sind aber nicht Teil von Ihnen, und es macht keinen Sinn, wenn sie versuchen, von Ihnen zu erben.

Wenn Sie mit dem Schreiben von Unit - Tests nicht vertraut sind, würde ich vorschlagen , dass Sie beginnen sofort.

Übrigens
quelle
6
Produktionsklassen sollten nichts über Unit-Test-Klassen wissen. Und die Tests sollten nicht die Interna der getesteten Klasse sehen müssen; Eine gut gestaltete Klasse kann über ihre öffentliche API einem Unit-Test unterzogen werden. Wenn nicht, handelt es sich höchstwahrscheinlich nicht um eine gut gestaltete Klasse (z. B. versucht sie, zu viel zu tun, sodass ein Teil ihrer Funktionalität besser in eine separate Klasse extrahiert werden sollte).
Péter Török
4
@ Peter-Torok: Das hängt davon ab, was "gut gestaltet" für Sie bedeutet. Ein häufiger Fall, in dem ich privilegierten Zugriff möchte, besteht darin, Mocks zu injizieren, um zu verfolgen, ob Aktionen ausgeführt wurden, die ausgeführt werden sollten. Das Offenlegen dieser Teile der Objektstruktur in der öffentlichen API scheint eine schwerwiegende Verletzung der Kapselung zu sein. Daher möchte ich häufig, dass Unit-Test-Code privilegierten Zugriff hat.
Btilly
1
@btilly: Wenn Sie einen Mock einfügen möchten, können Sie einen Parameter im Konstruktor verwenden, ohne auf die Interna der Klasse zugreifen zu müssen.
Giorgio
1
@ PéterTörök GTest ist anderer Meinung als Sie. In einigen Fällen wäre ein Test mit Freunden hilfreich. Beispielsweise möchte man möglicherweise die Äquivalenz von zwei Objekten derselben Klasse vergleichen, ohne eine Äquivalenzoperation in der API offenzulegen (möglicherweise aus Sicherheitsgründen).
VF1
1
@Giorgio Ja, Sie können die Informationen, die Sie einem Unit-Test unterziehen möchten, öffentlich machen. Aber jetzt ist Ihre öffentliche API gerade gewachsen und kann Sie daran hindern, Entscheidungen zu entwerfen, die in Zukunft unklug wären. Anders ausgedrückt, es ist angebracht, Unit-Tests für die Randfälle Ihrer aktuellen Implementierung durchzuführen. Es ist nicht immer angebracht, diese Detailgenauigkeit den Verbrauchern Ihrer Klasse zugänglich zu machen.
Btilly
0

Diese Frage sollte sich auf Stackoverflow beziehen. Auf jeden Fall verwenden Sie einen Freund nur, wenn Sie die privaten Teile Ihrer Klasse mit einer anderen Klasse (oder Klassen) teilen möchten, aber nicht mit anderen. Wenn Sie sie öffentlich machen, kann jeder Ihre privaten Teile sehen (Wortspiel beabsichtigt ;-P). Es gibt zwei wichtige Einschränkungen, die den Datenschutz erzwingen: 1) Sie müssen angeben, wer Ihr Freund ist. Niemand sonst kann ein Freund sein. 2) Sie können kein "freundliches" Verhalten in den Unterklassen der Freundesklasse erben

DPD
quelle
1
Sokratische Methodenfrage: Und warum ist es schlecht, wenn jeder Ihre privaten Teile sieht?
Larry Coleman
3
@ Larry: hängt davon ab, ob es geschmackvoll gemacht wird oder für den Schockwert: D
Matt Ellen
@Matt: Gibt es also eine geschmackvolle Verwendung öffentlicher Variablen?
Larry Coleman
@Larry: (nicht euphemistisch) Wenn Sie den Status des Objekts außerhalb der Kontrolle des Objekts ändern, befindet sich das Objekt nicht in einem ungültigen Status. Ich denke, Ereignisse in c # zählen in dieser Hinsicht.
Matt Ellen
0

Eine typische Verwendung besteht darin, Zugriff auf Funktionen zu gewähren, die Teil der Klassenschnittstelle sind, aber dank anderer Regeln nicht wirklich Teil der eigentlichen Klasse sein können. Inserter / Extraktoren für iostreams sind ein klassisches Beispiel:

namespace whatever { 
    class something { 
        // ...    
        friend std::ostream &operator<<(std::ostream &os, something const &thing);
        friend std::istream &operator>>(std::istream &is, something &thing);
    };
}

Es funktioniert nicht, diese Operatoren zu Mitgliedern der Klasse zu machen. Eine E / A-Operation sieht ungefähr so ​​aus:

whatever::something thing;

std::cout << thing;

Für einen Operator, der als Mitgliedsfunktion implementiert ist, wird dies wie folgt gelöst:

std::cout.operator<<(thing);

Das heißt, die Funktion müsste Mitglied von sein std::cout, nicht von something. Da wir nicht std::ostreamständig Änderungen vornehmen möchten , besteht unsere einzig sinnvolle Option darin, den Operator mit einer freien Funktion anstelle einer Mitgliedsfunktion zu überladen. Das lässt uns zwei Möglichkeiten: entweder die Kapselung vollständig ignorieren und alles in der Klasse öffentlich machen oder es privat halten, aber Zugriff auf die wenigen Dinge gewähren, die es wirklich brauchen.

Eine andere Klasse zu einem Freund zu machen, ist eher selten. In diesem Fall erstellen Sie normalerweise etwas in der Größenordnung eines Moduls oder Subsystems - eine Reihe von Klassen, die zusammenarbeiten und einen gewissen besonderen Zugriff aufeinander haben, der der Welt insgesamt nicht gewährt wird.

Jerry Sarg
quelle
0

Es gibt ein ziemlich gutes Beispiel für die Notwendigkeit von Freundklassen in dieser Anfrage für freundähnliche Klassen in C # , einer Sprache, in der sie nicht vorhanden sind.

Hier im Großhandel zitiert:

Zugriffsmodifikatoren in .NET wurden mit der Idee entwickelt, dass Sie Ihren Mitarbeitern vertrauen können, dass sie wissen, wie der Code funktioniert, und daher nicht ausrutschen und einen Fehler verursachen.

Diese Idee ist jedoch fehlerhaft. Die Softwareentwicklung ist so kompliziert, dass ein erfahrener Programmierer weiß, dass er sich selbst nicht einmal vertrauen kann! Aus diesem Grund bevorzugen erfahrene Programmierer das Schreiben von Code, der von Natur aus nicht durch versehentlichen Missbrauch beschädigt werden kann. .NET bietet die Tools, um dies zu tun, wenn nur eine Klasse beteiligt ist. In dem Moment, in dem ein Programmierer versucht, einen kugelsicheren Satz von zwei oder drei interagierenden Klassen zu erstellen, bricht dies zusammen.

Der von .NET beabsichtigte Ansatz besteht darin, die freigegebenen Mitglieder als "intern" zu markieren, wodurch die Klassen mit den Interna der anderen interagieren können. Leider erlaubt dies auch jeder anderen Klasse in der Versammlung , sich mit den Einbauten herumzuschlagen, sei es aus Versehen oder absichtlich.

Mads Torgersen, PM von C # Language, hat sogar auf diesen Vorschlag geantwortet und erklärt, dass das Anliegen zwar berechtigt ist, sich jedoch nicht sicher ist, welche spezifische Implementierung dort vorgeschlagen wird.

C ++ friendist nur eine der Möglichkeiten, um das beschriebene Problem zu lösen.

enverpex
quelle
0

Wenn Sie möchten, dass Ihre Klassen monogame Beziehungen haben.

Zum Beispiel, wenn Sie nicht möchten, dass unbekannte Klassen auf ihre Privaten zugreifen, ihre Partner jedoch Zugriff benötigen.

Danny Varod
quelle
Ähnliche Anwendungsfälle wie bei internen in C # und Paketen in Java.
Danny Varod
0

Ich habe festgestellt, dass Freundklassen in C ++ nützlich sind, wenn ich in Java den Paketzugriff verwendet hätte. Das heißt, ich habe eine Reihe von Klassen, die so eng miteinander verbunden sind, dass sie als Einheit in derselben Quelldatei (en) geschrieben und verwaltet werden. Jeder, der an einem von ihnen arbeitet, arbeitet wirklich an allen. Die Invarianten, die sie pflegen, pflegen sie zusammen. Es ist sinnvoll, dass sie die Interna des anderen sehen, damit sie ihre gemeinsame Invariante beibehalten können.

Was keinen Sinn ergibt, ist ein geschützter Zugang. Wenn es nicht sicher ist, einen Teil meiner Interna der Öffentlichkeit zugänglich zu machen, ist es nicht sicher, ihn den Jackanapes auszusetzen, die später in einem völlig unabhängigen Projekt auftauchen und behaupten, meine Unterklasse zu sein. Lassen Sie Unterklassen meine öffentliche Schnittstelle verwenden, genau wie alle anderen. (Es sei denn, sie sind Freunde, aber in diesem Fall habe ich sie geschrieben und in derselben Quelle implementiert, sodass sie kaum aus einem anderen Projekt stammen.)

Ganbustein
quelle
-1

Nehmen wir an, es gibt eine externe Funktion, bei der Sie Operationen an privaten oder geschützten Mitgliedern zweier verschiedener Klassen in Ihrem Programm ausführen müssen. Damit diese Funktion Zugriff auf diese Klassenmitglieder erhält und diese tatsächlich verwendet, müssen Sie sie zu einem Freund beider Klassen machen. Wenn man bedenkt, dass wenn man es zu einem Freund dieser beiden Klassen macht, es nicht zu einem Mitglied der Klassen wird, erhält man nur die Vorstufe, sich auf die privaten oder geschützten Mitglieder dieser Klassen zu beziehen. Ich kann sagen, dass Sie eine Friend-Funktion verwenden müssten, wenn Sie Objekte aus zwei verschiedenen Klassen bearbeiten möchten. Obwohl die Überbeanspruchung von Freundfunktion und Klasse den Wert der Kapselung verringern kann.

Fiko V.
quelle
2
Dieser Beitrag ist ziemlich schwer zu lesen (Textwand). Hätten Sie etwas dagegen bearbeiten sie in eine bessere Form ing?
Mücke