Class -> friend oder nicht



  • KasF schrieb:

    Allgeimein: Implementiere Vergleichsoperatoren ( ==, !=, <, >, <= ...) immer global, sowie auch op+ op- op/ op* und implementiere diese wiederum mit op+= etc.
    D.h. Modifizierende Operatoren in der Klasse nicht modifizierende außerhalb ...

    Interessanterweise erspart man sich dann meistens sogar das "friend". Warum es sich trotzdem eingebürgert hat? Den einzigen syntaktischen Vorteil, den ich dann noch sehe, ist bei der Deklaration von template-Operatoren.



  • davie schrieb:

    Interessanterweise erspart man sich dann meistens sogar das "friend". Warum es sich trotzdem eingebürgert hat? Den einzigen syntaktischen Vorteil, den ich dann noch sehe, ist bei der Deklaration von template-Operatoren.

    Ja, da hast du Recht. Ich wollte aber DIE friend-Geschichte nicht in dieser friend-Geschichte erzählen. 😉



  • @davie: Hmm was hat man da wohl noch für nen Vorteil ... man betrachte Sichtbarkeiten usw ...


  • Mod

    davie schrieb:

    Interessanterweise erspart man sich dann meistens sogar das "friend". Warum es sich trotzdem eingebürgert hat? Den einzigen syntaktischen Vorteil, den ich dann noch sehe, ist bei der Deklaration von template-Operatoren.

    Weil es den Operator in offensichtlicher Weise dem Interface der Klasse zuordnet?
    Zudem erlaubt es die Definition der Funktion innerhalb der Klassendefinition (dabei spart man sogar noch ein inline).
    Und dann ist da noch das Namelookup...



  • Danke 🤡



  • camper schrieb:

    davie schrieb:

    Interessanterweise erspart man sich dann meistens sogar das "friend". Warum es sich trotzdem eingebürgert hat? Den einzigen syntaktischen Vorteil, den ich dann noch sehe, ist bei der Deklaration von template-Operatoren.

    Weil es den Operator in offensichtlicher Weise dem Interface der Klasse zuordnet?

    punkt für dich. aber das kann ich auch anders ausdrücken (mit einem eigenen header für die klasse und ihr zugehörige funktionen z.b.)

    Zudem erlaubt es die Definition der Funktion innerhalb der Klassendefinition (dabei spart man sogar noch ein inline).

    da liegt doch wohl der hauptvorteil eher darin, dass man sich den operator als template erspart (was allerdings auch oft sehr verwirrend sein kann, wenn man denkt, einen template-operator definiert zu haben, der in wahrheit kein template ist)

    Und dann ist da noch das Namelookup...

    worauf genau beziehst du dich jetzt? namelookup der namen in der friend deklaration? ist doch auch eher unintuitiv.



  • Hmm naja wofür gibt es normalerweise eine friend-Beziehung? Er bezieht sich da auf das selbe, das ich auch angemerkt habe.



  • Ich spam mal gleich hier weiter, hab gerad noch nen mir völlig unbegreifliches Problem...

    Ich nutze VS (und Win XP)

    //HEADER - hab ich aber wieder mit in die *.cpp reingemacht, weil ich dachte, dass ich da vll iwas verhauen hätte -.-
    //includes (vector, iterator, iostream + ne helper.cpp, die mir paar Aufgaben abnimmt
    
    typedef std::vector <double> row;
    typedef std::vector<row>::const_iterator cit_row;
    typedef std::vector<row>::iterator it_row;
    typedef row::const_iterator cit_ceil;
    typedef row::iterator it_ceil;
    
    class MyMatrix
    {
    	private:
    		unsigned int Spalten, Zeilen;
    		std::vector < row > values;
    		bool ok;
    	public:
    		MyMatrix (void);
    		MyMatrix (bool init);
    		MyMatrix (const MyMatrix & matrix);
    		~MyMatrix (void);
    		unsigned int GetSpalten (void) const {return Spalten;};
    		unsigned int GetZeilen (void) const {return Zeilen;};
    
    		void SetSpalten (unsigned int spalten) {Spalten = spalten;};
    		void SetZeilen (unsigned int zeilen) {Zeilen = zeilen;};
    
    		bool IsOK (void) const {return ok;};
    
    		friend std::ostream& operator << (std::ostream & out, const MyMatrix & matrix);
    		void operator = (const MyMatrix & matrix2);
    		MyMatrix& operator + (const MyMatrix & matrix);
    		MyMatrix& operator += (const MyMatrix & matrix2);
    		bool operator == (const MyMatrix & matrix2);
    		bool operator != (const MyMatrix & matrix2);
    };
    
    //Code an sich:
    MyMatrix::MyMatrix (void) : Spalten (0), Zeilen (0), ok (true)
    {}
    //etc - tut hier jz aber nix zur Sache
    

    Und da bekomm ich für jede Funktion, die ich so definieren will wieder folgende Fehlermeldung:

    matrizen.obj : error LNK2005: "public: __thiscall MyMatrix::MyMatrix(void)" (??0MyMatrix@@QAE@XZ) ist bereits in main.obj definiert.
    

    Also gelesen hab ich es schon - verstehen tu ich es auch - aber eben nur grammatikalisch - oder gar nicht ^^
    Also ich weiß nicht, was er mit den *.obj - Dateien will - die legt er auch immer erst beim Compilieren an - also am Namen an sich liegt es denke auch nicht...

    Danke schon mal ^^

    Bye Tom

    //edit:
    Ist erstellt als ein ganz normales leeres WIN32-Konsolen-Projekt...



  • unskilled schrieb:

    Ich spam mal gleich hier weiter, hab gerad noch nen mir völlig unbegreifliches Problem...

    ...

    Und da bekomm ich für jede Funktion, die ich so definieren will wieder folgende Fehlermeldung:

    matrizen.obj : error LNK2005: "public: __thiscall MyMatrix::MyMatrix(void)" (??0MyMatrix@@QAE@XZ) ist bereits in main.obj definiert.
    

    Auf anhieb fallen mir schon einmal fehlende Include-Guards auf. Sprich etwas wie dem folgenden:

    Header:

    #if !defined(PROJEKTNAME_HEADERNAME_HEADER)
    #defined PROJEKTNAME_HEADERNAME_HEADER
    
    // <-- Hier den eigentlichen inhalt des Headers einfügen
    
    #endif
    


  • camper schrieb:

    davie schrieb:

    Interessanterweise erspart man sich dann meistens sogar das "friend". Warum es sich trotzdem eingebürgert hat? Den einzigen syntaktischen Vorteil, den ich dann noch sehe, ist bei der Deklaration von template-Operatoren.

    Weil es den Operator in offensichtlicher Weise dem Interface der Klasse zuordnet?

    Hm, nach dem interface principle braucht der op dazu weder friend noch innerhalb der Klassendefinition zu sein. Eigentlich sagt ja auch schon der gesunde Menschenverstand, dass eine freie Funktion, die im gleichen Header wie die Klasse X kommt und ein Argument vom Typ X hat, wohl zu deren Schnittstelle gehört.

    Zudem erlaubt es die Definition der Funktion innerhalb der Klassendefinition (dabei spart man sogar noch ein inline).

    Sind Nichtmember, die innerhalb einer Klasse definiert werden, auch implizit inline deklariert? Dachte das gelte nur für Member.

    Und dann ist da noch das Namelookup...

    Inwiefern spielt das da eine Rolle? Bzw. in welchen Fällen findet das Namelookup für in-der-Klasse-Deklarationen etwas, was es bei normalen freien nonfreinds nicht findet?


  • Mod

    pumuckl schrieb:

    camper schrieb:

    davie schrieb:

    Interessanterweise erspart man sich dann meistens sogar das "friend". Warum es sich trotzdem eingebürgert hat? Den einzigen syntaktischen Vorteil, den ich dann noch sehe, ist bei der Deklaration von template-Operatoren.

    Weil es den Operator in offensichtlicher Weise dem Interface der Klasse zuordnet?

    Hm, nach dem interface principle braucht der op dazu weder friend noch innerhalb der Klassendefinition zu sein. Eigentlich sagt ja auch schon der gesunde Menschenverstand, dass eine freie Funktion, die im gleichen Header wie die Klasse X kommt und ein Argument vom Typ X hat, wohl zu deren Schnittstelle gehört.

    Mit dem gesunden Menschenverstand ist das so eine Sache. Wie man in diesem Forum oft feststellen kann, fällt es vielen schwer, freie und Memberfunktionen nicht für etwas grundsätzlich Verschiedenes zu halten.

    Zudem erlaubt es die Definition der Funktion innerhalb der Klassendefinition (dabei spart man sogar noch ein inline).

    Sind Nichtmember, die innerhalb einer Klasse definiert werden, auch implizit inline deklariert? Dachte das gelte nur für Member.

    Auch für friends. Sonst wäre es ja nicht möglich, die Klasse in mehr als einer ÜE zu verwenden.

    Und dann ist da noch das Namelookup...

    Inwiefern spielt das da eine Rolle? Bzw. in welchen Fällen findet das Namelookup für in-der-Klasse-Deklarationen etwas, was es bei normalen freien nonfreinds nicht findet?

    Umgekehrt und nur wenn die Funktion in der friend-Deklaration auch definiert wird. Eine friend-Funktion wird durch unqualifiziertes Lookup im umschließenden Namensraum nur gefunden, wenn sie in diesem Namensraum zuvor deklariert wurde. Ein friend, der ausschließlich in der Klasse deklariert und definiert wird, wird folglich durch unqualifiziertes Lookup nur gefunden, wenn man sich im Scope der Klasse befindet. Das ist vorteilhaft, weil man so einigen ungewollten Kollisionen aus dem Weg gehen kann. z.B.

    struct foo; // kein op==
    struct bar
    {
        Bar(const Foo&);
        ...
    };
    bool operator==(const Bar&, const Bar&);
    
    bool foo(const foo& x, const foo& y)
    {
        return x == y; // ruft bool operator==(const Bar&, const Bar&) auf
    }
    

    Hier rufen wir eindeutig den falschen Operator auf, auf die gleiche Weise können Mehrdeutigkeiten entstehen, die es nicht geben sollte. Bei friend-Definition in der Klasse ohne Redeklaration im Namensraum, würde dieser Operator nicht gefunden werden. Anders wenn wenigstens ein Operator vom Typ bar ist, dann wird dieser Operator per ADNL gefunden werden, und das ist dann ja auch richtig.



  • Auf anhieb fallen mir schon einmal fehlende Include-Guards auf.

    Hab ich - hab ich nur nicht mitgepostet - wird auch nur einmal included - von daher sollte (!?) das auch egal sein ^^

    Oder ist das falsch:

    #ifndef MATRIZEN_CPP
    #define MATRIZEN_CPP
    
    //benötigte includes, klassen-deklaration und definition der fkt
    
    #endif //#ifndef MATRIZEN_CPP
    


  • camper schrieb:

    struct foo; // kein op==
    struct bar
    {
        Bar(const Foo&);
        ...
    };
    bool operator==(const Bar&, const Bar&);
    
    bool foo(const foo& x, const foo& y)
    {
        return x == y; // ruft bool operator==(const Bar&, const Bar&) auf
    }
    

    Hier rufen wir eindeutig den falschen Operator auf, auf die gleiche Weise können Mehrdeutigkeiten entstehen, die es nicht geben sollte.

    ich dachte schon fast, dass du *darauf* hinauswillst. aber ist das tatsächlich der falsche operator, der aufgerufen wird?
    ist es tatsächlich sinnvoll, folgenden drei fällen unterschiedliche semantik zu geben?

    //erstens
    struct bar;
    bool operator(bar const&, bar const&);
    //...
    struct foo {};
    
    struct bar
    {
       bar (foo const&);
       friend bool operator (bar const&, bar const&);
    }
    
    //zweitens
    struct foo {};
    
    struct bar
    {
       bar (foo const&);
       friend bool operator (bar const&, bar const&) { }
    }
    
    //drittens
    struct bar
    {
       bar (foo const&);
       friend bool operator (bar const&, bar const&);
    };
    
    bool operator (bar const&, bar const&)
    

    wenn die automatische umwandlung von foo nach bar unterdrücken will, sollte man dann nicht lieber auf den umwandlungskonstruktor verzichten?

    struct bar
    {
       explicit bar(foo const&);
    };
    


  • man vergebe mir die syntaktischen fehler und das fehlende == im eifer des gefechts.


Anmelden zum Antworten