wake-up-neo.com

Wo würden Sie eine Freundesfunktion gegenüber einer statischen Memberfunktion verwenden?

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()).

58
Swapna

Abschnitt 11.5 "Die C++ - Programmiersprache" von Bjarne Stroustrup besagt, dass gewöhnliche Memberfunktionen drei Dinge erhalten:

  1. zugang zu internen Informationen der Klasse
  2. sind im Umfang der Klasse
  3. muss für eine Instanz aufgerufen werden

friends erhalten nur 1.

static-Funktionen erhalten 1 und 2.

63
pm100

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):

  • Die Deklaration im Namensraum N vor der Definition von A;
  • Die friend -Deklaration in der A -Definition;
  • Die Definition von 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.

41
Andy Prowl

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.

10
Alok Save

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.

4
synepis

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:

  • als Rückruffunktionen
  • mitglieder der Klasse zu manipulieren
  • um konstante Daten abzurufen, die Sie nicht in Ihrer Headerdatei auflisten möchten
  • 4
    thebretness

    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.

    2
    Jagannath
    • 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
      };
      
    2
    nav

    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.

    2
    tp1

    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.

    1
    UserXYZ

    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.

    1
    San Jacinto

    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 !!

    0
    newbieprog

    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.

    0
    tzenes

    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

    0
    lollinus