Wir machen eine Nicht-Mitglieder-Funktion zu einem Freund einer Klasse, wenn diese Zugriff auf die privaten Mitglieder dieser Klasse haben soll. Dies gibt ihm die gleichen Zugriffsrechte wie eine statische Memberfunktion. Bei beiden Alternativen erhalten Sie eine Funktion, die keiner Instanz dieser Klasse zugeordnet ist.
Wann müssen wir eine Friend-Funktion nutzen? Wann müssen wir eine statische Funktion verwenden? Wenn beides praktikable Optionen zur Lösung eines Problems sind, wie bewerten wir deren Eignung? Gibt es eine, die standardmäßig bevorzugt werden sollte?
Wenn zum Beispiel eine Factory implementiert wird, die Instanzen der Klasse foo
erstellt, die nur über einen privaten Konstruktor verfügt, sollte diese Factory-Funktion ein statisches Member von foo
sein (Sie würden foo::create()
aufrufen) oder sollte sie eine Friend-Funktion sein (Sie würden create_foo()
).
Abschnitt 11.5 "Die C++ - Programmiersprache" von Bjarne Stroustrup besagt, dass gewöhnliche Memberfunktionen drei Dinge erhalten:
friend
s erhalten nur 1.
static
-Funktionen erhalten 1 und 2.
Die Frage scheint sich mit der Situation zu befassen, in der der Programmierer eine Funktion einführen muss, die nicht auf jeder Instanz funktioniert einer Klasse (daher die Möglichkeit, eine static
Mitgliedsfunktion zu wählen). Daher beschränke ich diese Antwort auf die folgende Entwurfssituation, in der die Auswahl zwischen einer statischen Funktion f()
und einer freien Funktion f()
besteht:
struct A
{
static void f(); // Better this...
private:
friend void f(); // ...or this?
static int x;
};
int A::x = 0;
void A::f() // Defines static function
{
cout << x;
}
void f() // Defines friend free function
{
cout << A::x;
}
int main()
{
A::f(); // Invokes static function
f(); // Invokes friend free function
}
Ohne vorher etwas über die Semantik von f()
und A
zu wissen (darauf komme ich später zurück) Dieses eingeschränkte Szenario hat eine einfache Antwort: Die Funktion static
ist vorzuziehen . Ich sehe zwei Gründe dafür.
GENERISCHE ALGORITHMEN:
Der Hauptgrund ist, dass eine Vorlage wie die folgende geschrieben werden kann:
template<typename T> void g() { T::f(); }
Wenn wir zwei oder mehr Klassen hätten, deren Schnittstelle eine static
-Funktion f()
enthält, könnten wir eine einzelne Funktion schreiben, die f()
generisch für eine solche Klasse aufruft.
Es gibt keine Möglichkeit, eine äquivalente generische Funktion zu schreiben, wenn wir f()
zu einer freien Nicht-Member-Funktion machen. Obwohl es wahr ist, dass wir f()
in einen Namespace setzen könnten, so dass die N::f()
-Syntax verwendet werden könnte, um die A::f()
-Syntax nachzuahmen, wäre es dennoch unmöglich Um eine Template-Funktion wie g<>()
zu schreiben, da Namespace-Namen keine gültigen Template-Argumente sind.
REDUNDANTE ERKLÄRUNGEN:
Der zweite Grund ist, dass wir , wenn wir die freie Funktion f()
in einen Namespace setzen würden, nicht inline sein könnten Definition direkt in der Klassendefinition, ohne eine andere Deklaration für f()
einzufügen:
struct A
{
static void f() { cout << x; } // OK
private:
friend void N::f() { cout << x; } // ERROR
static int x;
};
Um dies zu beheben, müssen wir der Definition der Klasse A
die folgende Deklaration voranstellen:
namespace N
{
void f(); // Declaration of f() inside namespace N
}
struct A
{
...
private:
friend void N::f() { cout << x; } // OK
...
};
Dies widerspricht jedoch unserer Absicht, f()
an nur einer Stelle deklarieren und definieren zu lassen.
Wenn wir außerdem f()
separat deklarieren und definieren möchten, während f()
in einem Namespace verbleibt, müssen wir vor der Klassendefinition noch eine Deklaration für f()
einfügen für A
: Andernfalls würde sich der Compiler darüber beschweren, dass f()
im Namespace N
deklariert werden musste, bevor der Name N::f
lauten könnte legal verwendet.
Wir hätten also nun f()
an drei getrennten Stellen anstelle von nur zwei (Deklaration und Definition):
N
vor der Definition von A
;friend
-Deklaration in der A
-Definition;f()
im Namespace N
.Der Grund, warum die Deklaration und Definition von f()
in N
(im Allgemeinen) nicht verknüpft werden kann, ist, dass f()
auf die Interna von A
und zugreifen soll Daher muss die Definition von A
gesehen werden, wenn f()
definiert ist. Wie bereits erwähnt, muss die Deklaration von f()
innerhalb von N
gesehen werden, bevor die entsprechende friend
-Deklaration innerhalb von A
erfolgt. Dies zwingt uns effektiv dazu, die Deklaration und die Definition von f()
aufzuteilen.
SEMANTISCHE ÜBERLEGUNGEN:
Obwohl die beiden oben genannten Punkte allgemein gültig sind, gibt es Gründe, warum man es vorziehen könnte, f()
als static
zu deklarieren, anstatt es zu friend
von A
zu machen, oder umgekehrt getrieben vom Universum des Diskurses.
Zur Verdeutlichung ist es wichtig, die Tatsache hervorzuheben, dass eine Mitgliedsfunktion einer Klasse, ob sie nun static
oder nicht static
ist, logisch Teil von diese Klasse. Es trägt zu seiner Definition bei und liefert damit eine konzeptionelle Charakterisierung.
Andererseits ist eine friend
-Funktion, obwohl sie Zugriff auf die internen Mitglieder der Klasse hat, mit der sie befreundet ist, immer noch ein Algorithmus, der logisch extern ist zur Definition der Klasse.
Eine Funktion kann friend
von mehr als einer Klasse sein, aber sie kann nur Mitglied von einer sein.
Daher möchte der Designer in einer bestimmten Anwendungsdomäne möglicherweise die - Semantik sowohl der Funktion als auch der Klasse berücksichtigen, wenn er entscheidet, ob die erstere als friend
definiert wird. oder ein Mitglied des letzteren (dies gilt nicht nur für static
Funktionen, sondern auch für nicht static
Funktionen, bei denen andere Spracheinschränkungen eingreifen können).
Trägt die Funktion logisch zur Charakterisierung einer Klasse und/oder ihres Verhaltens bei oder handelt es sich vielmehr um einen externen Algorithmus? Diese Frage kann ohne Kenntnis der jeweiligen Anwendungsdomäne nicht beantwortet werden.
TASTE:
Ich glaube, dass jedes andere Argument, das gerade angegeben wurde, rein aus einer Geschmackssache stammt: sowohl der freie friend
als auch der static
Mitgliedsansatz Lassen Sie zu, dass die Schnittstelle einer Klasse in einem einzelnen Punkt (der Klassendefinition) klar angegeben wird, so dass sie hinsichtlich des Designs äquivalent sind (natürlich modulo der obigen Beobachtungen).
Die verbleibenden Unterschiede sind stilistisch: ob wir das Schlüsselwort static
oder das Schlüsselwort friend
schreiben möchten, wenn wir eine Funktion deklarieren, und ob wir das Gültigkeitsbereichskennzeichen der Klasse A::
Schreiben möchten, wenn wir das definieren Klasse anstelle des Bereichsqualifizierers N::
für den Namespace. Daher werde ich darauf nicht weiter eingehen.
Der Unterschied drückt deutlich die Absicht der Beziehung zwischen der Klasse und der Funktion aus.
Sie verwenden friend
, wenn Sie absichtlich eine starke Kopplung und besondere Beziehung zwischen zwei nicht verknüpften Klassen oder zwischen einer Klasse und einer Funktion angeben möchten.
Sie verwenden die Funktion static
, wenn die Funktion logisch ein Teil der Klasse ist , der sie angehört.
Statische Funktionen werden verwendet, wenn Sie eine Funktion wünschen, die für jede Instanz einer Klasse gleich ist. Solche Funktionen haben keinen Zugriff auf "this" -Zeiger und können daher nicht auf nicht statische Felder zugreifen. Sie werden häufig verwendet, wenn Sie eine Funktion wünschen, die ohne Instanziierung der Klasse verwendet werden kann.
Freundesfunktionen sind Funktionen, die nicht in der Klasse enthalten sind, und Sie möchten ihnen Zugriff auf private Mitglieder Ihrer Klasse gewähren.
Bei diesem (statischen vs. Freund) geht es nicht darum, eins gegen das andere zu verwenden, da es keine Gegensätze sind.
Freundesfunktionen (und Klassen) können auf die privaten und geschützten Mitglieder Ihrer Klasse zugreifen. Es gibt selten einen guten Grund für die Verwendung einer Friend-Funktion oder -Klasse. Vermeiden Sie sie im Allgemeinen.
Statische Funktionen dürfen nur auf statische Daten (dh Daten mit Klassenbereich) zugreifen. Sie können aufgerufen werden, ohne eine Instanz Ihrer Klasse zu erstellen. Statische Funktionen sind ideal für Situationen, in denen sich alle Instanzen Ihrer Klasse gleich verhalten sollen. Sie können sie verwenden:
Der Standard verlangt, dass operator = () [] und -> Mitglieder und klassenspezifisch sein muss
Operatoren new, new [], delete und delete [] müssen statische Member sein. Wenn die Situation
entsteht, wo wir das Objekt der Klasse nicht benötigen, um eine Funktion aufzurufen, und dann make
die Funktion statisch. Für alle anderen Funktionen:
wenn für eine Funktion die Operatoren = () [] und -> für Stream-E/A erforderlich sind,
oder wenn es Typkonvertierungen für das Argument ganz links benötigt, oder wenn es nur mit der öffentlichen Schnittstelle der Klasse implementiert werden kann, Nichtmitglied machen (und Freund, falls in den ersten beiden Fällen erforderlich)
wenn es sich virtuell verhalten muss,
Fügen Sie eine virtuelle Memberfunktion hinzu, um das virtuelle Verhalten bereitzustellen
und diesbezüglich umzusetzen
sonst
mache es ein Mitglied.
Ein Grund, einen Freund einem statischen Member vorzuziehen, ist, wenn die Funktion in Assembly (oder einer anderen Sprache) geschrieben werden muss.
Zum Beispiel können wir immer eine externe "C" -Freundfunktion in unserer .cpp-Datei deklarieren lassen
class Thread;
extern "C" int ContextSwitch(Thread & a, Thread & b);
class Thread
{
public:
friend int ContextSwitch(Thread & a, Thread & b);
static int StContextSwitch(Thread & a, Thread & b);
};
Und später in Assembly definiert:
.global ContextSwitch
ContextSwitch: // ...
retq
Technisch gesehen könnten wir dazu eine statische Member-Funktion verwenden, aber die Definition in Assembly ist aufgrund des Namensmanglings nicht einfach ( http://en.wikipedia.org/wiki/Name_mangling )
Eine andere Situation ist, wenn Sie Operatoren überlasten müssen. Das Überladen von Operatoren kann nur durch Freunde oder nicht statische Mitglieder erfolgen. Wenn das erste Argument des Operators keine Instanz derselben Klasse ist, funktioniert auch das nicht statische Member nicht. Freund wäre die einzige Option:
class Matrix
{
friend Matrix operator * (double scaleFactor, Matrix & m);
// We can't use static member or non-static member to do this
};
Die statische Funktion kann nur auf Mitglieder der Klasse one zugreifen. Die Friend-Funktion hat Zugriff auf mehrere Klassen, wie im folgenden Code erläutert:
class B;
class A { int a; friend void f(A &a, B &b); };
class B { int b; friend void f(A &a, B &b); };
void f(A &a, B &b) { std::cout << a.a << b.b; }
f () kann auf Daten der Klassen A und B zugreifen.
Eine Friend-Funktion kann nicht vererbt werden, während eine statische Funktion ausgeführt werden kann. Wenn ein Ziel sowohl mit der statischen Funktion als auch mit der Friend-Funktion erreicht werden kann, denken Sie daran, ob Sie es erben möchten oder nicht.
Sie würden eine statische Funktion verwenden, wenn die Funktion den Status einer bestimmten Instanz der Klasse nicht lesen oder ändern muss (dh, Sie müssen das Objekt im Speicher nicht ändern) oder wenn Sie einen Funktionszeiger verwenden müssen eine Memberfunktion einer Klasse. Wenn Sie in dieser zweiten Instanz den Status des residenten Objekts ändern müssen, müssen Sie this
übergeben und die lokale Kopie verwenden. Im ersten Fall kann eine solche Situation eintreten, in der die Logik zur Ausführung einer bestimmten Aufgabe nicht vom Zustand eines Objekts abhängt, jedoch wäre Ihre logische Gruppierung und Verkapselung ein Mitglied einer bestimmten Klasse.
Sie verwenden eine Friend-Funktion oder -Klasse, wenn Sie Code erstellt haben, der kein Mitglied Ihrer Klasse ist und kein Mitglied Ihrer Klasse sein sollte, der jedoch einen legitimen Zweck zum Umgehen der privaten/geschützten Kapselungsmechanismen hat. Ein Grund dafür kann sein, dass Sie zwei Klassen haben, für die einige gemeinsame Daten erforderlich sind, um die Logik zweimal zu codieren. Dies wäre schlecht. Wirklich, ich habe diese Funktionalität nur in vielleicht 1% der Klassen verwendet, die ich jemals codiert habe. Es wird selten gebraucht.
Friend-Funktionen können auf die privaten und geschützten Mitglieder anderer Klassen zugreifen. Mit diesen Funktionen können Sie auf alle Daten zugreifen, die privat oder öffentlich sind. Mit Friend-Funktionen werden auf diese Daten zugegriffen, die mit statischen Methoden nicht möglich sind .
Diese Methoden werden statisch gemacht, die so oft aufgerufen werden, dass innerhalb jedes Objekts eine andere Position angegeben wird, da sie zu kostspielig werden (in Bezug auf den Speicher). Dies kann mit Hilfe des Beispiels deutlich gemacht werden: Let Der Name der Klasse ist fact und ihr Datenelement n (was eine ganze Zahl darstellt, deren Fakultät betroffen ist) In diesem Fall wäre es eine kluge Entscheidung, find_factorial () als statisch zu deklarieren.
Sie werden als Callback-Funktionen verwendet, um Member im Klassenbereich zu bearbeiten , um konstante Daten abzurufen, die Sie nicht in Ihrer Headerdatei auflisten möchten
Nun sind wir mit den folgenden Fragen klar.
Wenn eine Freundesfunktion verwendet wird? Wenn eine statische Funktion verwendet wird?
Wenn beides praktikable Optionen zur Lösung eines Problems sind, können wir ihre Eignung in Bezug auf die Zugänglichkeit (Zugänglichkeit von privaten Daten) und die Speichereffizienz aufwerfen Wir brauchen eine bessere Speicherverwaltung, und manchmal sind wir mit dem Datenumfang beschäftigt.
Zum Beispiel: Foo :: create () wird gegenüber create_foo () bevorzugt, wenn die create () - Methode nach jeder kurzen Zeit aufgerufen wird und wir uns nicht für den Datenumfang interessieren (private Daten).
Wenn wir daran interessiert sind, die privaten Informationen von mehreren Klassen zu erhalten, wird create_foo () gegenüber foo :: create () bevorzugt.
Ich hoffe das würde dir helfen !!
Eine statische Funktion ist eine Funktion, die keinen Zugriff auf this
hat.
Eine Friend-Funktion ist eine Funktion, die auf private Mitglieder der Klasse zugreifen kann.
Statische Funktionen können auf viele verschiedene Arten verwendet werden.
Zum Beispiel als einfache Werksfunktion:
class Abstract {
private:
// no explicit construction allowed
Abstract();
~Abstract();
public:
static Abstract* Construct() { return new Abstract; }
static void Destroy(Abstract* a) { delete a; }
};
...
A* a_instance = A::Conctruct();
...
A::Destroy(a_instance);
Dies ist ein sehr vereinfachtes Beispiel, aber ich hoffe, es erklärt, was ich meinte.
Oder als Thread-Funktion, die mit Ihrer Klasse arbeitet:
class A {
public:
static void worker(void* p) {
A* a = dynamic_cast<A*>(p);
do something wit a;
}
}
A a_instance;
pthread_start(&thread_id, &A::worker, &a_instance);
....
Friend ist eine völlig andere Geschichte und ihre Verwendung ist genau so, wie sie von der Bretness beschrieben wird