friend Funktionen anderer Klassen
-
MFK schrieb:
Die Stream-Operatoren dürfen nicht Member der Klasse sein, deren Ein-/Ausgabe sie implementieren sollen, weil die jeweilige Klasseninstanz im zweiten Parameter übergeben werden muss. Wenn du aus den Operatoren Klassenmember machst, wird die Klasseninstanz implizit als erster Parameter übergeben. Darum ist dann auch nur noch ein Parameter notwenig - aber die Reihenfolge passt dann natürlich nicht.
Das mit friend und außerhalb ist also keine Laune, sondern Absicht.
Erstmal danke für die schnelle Antwort. Die Aufgabe besteht jedoch darin, die Überladung von << innerhalb der Klasse Player zu implementieren und diese dann in Lobby zur friend Funktion zu machen. Bei einer "normalen" Funktion ist das mit dem impliziten Parameter der Instanz schon klar, da würde ja dann auch *pNewPlayer->anyprintfunction() ohne Parameter ausreichen. Aber in dem Fall, dass ich direkt "cout << playerobj" implementieren will, muss ich doch eine Referenz auf playerobj haben, ebenso auf einen Stream auf der rechten Seite?! Lässt sich das also garnicht implementieren?
-
TestGuest schrieb:
Aber in dem Fall, dass ich direkt "cout << playerobj" implementieren will, muss ich doch eine Referenz auf playerobj haben, ebenso auf einen Stream auf der rechten Seite?!
Du brauchst den Stream auf der linken Seite. Ich sag's auch gern nochmal: Die Stream-Operatoren dürfen nicht Member der Klasse sein, deren Ein-/Ausgabe sie implementieren sollen.
-
TestGuest schrieb:
Erstmal danke für die schnelle Antwort. Die Aufgabe besteht jedoch darin, die Überladung von << innerhalb der Klasse Player zu implementieren und diese dann in Lobby zur friend Funktion zu machen. Bei einer "normalen" Funktion ist das mit dem impliziten Parameter der Instanz schon klar, da würde ja dann auch *pNewPlayer->anyprintfunction() ohne Parameter ausreichen. Aber in dem Fall, dass ich direkt "cout << playerobj" implementieren will, muss ich doch eine Referenz auf playerobj haben, ebenso auf einen Stream auf der rechten Seite?! Lässt sich das also garnicht implementieren?
Nein, bei cout << playerobj ist der Stream das linke Argument, man könnte es daher höchstenns als Member von std::ostream implementieren. Das ist aber natürlich nicht möglich, also muss es eine freie Fuktion sein. Zeig uns mal den Text der Aufgabe, vielleicht hast du da was missverstanden.
-
pumuckl schrieb:
Nein, bei cout << playerobj ist der Stream das linke Argument, man könnte es daher höchstenns als Member von std::ostream implementieren. Das ist aber natürlich nicht möglich, also muss es eine freie Fuktion sein. Zeig uns mal den Text der Aufgabe, vielleicht hast du da was missverstanden.
Ok, muss ich abtippen:
Improve the Lobby class from the Game Lobby program by writing a friend function of the Player class that allows a Player object to be sent to cout. Next, update the function that allows a Lobby object to be sent to cout so that it uses your new function for sending a Player object to cout.
Übrigens, in einem anderen Beispiel wurde der "=" Operator auch als Member überladen, hier mal der entsprechende Ausschnitt:
class Critter { public: Critter(const string& name = "", int age = 0); ~Critter(); //destructor prototype Critter(const Critter& c); //copy constructor prototype Critter& Critter::operator=(const Critter& c); //overloaded assignment op void Greet() const; private: string* m_pName; int m_Age; }; .... Critter& Critter::operator=(const Critter& c) //overloaded assignment op def { cout << "Overloaded Assignment Operator called\n"; if (this != &c) { delete m_pName; m_pName = new string(*(c.m_pName)); m_Age = c.m_Age; } return *this; }Im Prinzip ist das doch das Gleiche, oder?
PS: Wenn man Code mit copy&paste einfügt, wird er nach der ersten Vorschau mit vielen Leerzeilen versehen. Wie verhindert man das? Mein Editor ist gedit.
-
TestGuest schrieb:
Improve the Lobby class from the Game Lobby program by writing a friend function of the Player class that allows a Player object to be sent to cout.
Da steht nichts davon, dass du Player eine Methode hinzufügen sollst, sondern dass du eine friend-Funktion der Klasse schreiben sollst. Ein friend ist kein Member.
TestGuest schrieb:
Übrigens, in einem anderen Beispiel wurde der "=" Operator auch als Member überladen, hier mal der entsprechende Ausschnitt:
...
Im Prinzip ist das doch das Gleiche, oder?Nein, weil beim Zuweisungsoperator deine Klasseninstanz auf der linken Seite steht.
Manche Operatoren müssen Member sein, manche muss man als freie Funktionen anlegen.
-
Improve the Lobby class from the Game Lobby program by writing a friend function of the Player class that allows a Player object to be sent to cout. Next, update the function that allows a Lobby object to be sent to cout so that it uses your new function for sending a Player object to cout.
Steht doch gar nicht drinnen, dass das ne Member-Funktion sein soll.
Zur Info: Zum Interface einer Klasse gehören auch freie Funktionen, die auf der jeweiligen Klasse arbeiten! Somit ist auch eine freie Funktionen ein "improvement" deiner Klasse.TestGuest schrieb:
PS: Wenn man Code mit copy&paste einfügt, wird er nach der ersten Vorschau mit vielen Leerzeilen versehen. Wie verhindert man das? Mein Editor ist gedit.
Das liegt an der Editorkomonente die dein Browser verwendet. Du hast wahrscheinlich da Tabs im Code. Die werden beim Pasten in Leerzeichen umgewandelt. Und die Editorkomponente wird da wohl gaaanz viele reinbauen (8 Stück, kann das sein?).
-
Erstmal danke an Alle! Das heisst also, dass die Überladung ausserhalb der Klassendefinition und fast ebenso wie die Überladung fürs Lobby Objekt implementiert wird. Hier die entsprechenden Codefragmente:
class Lobby { friend ostream& operator<<(ostream& os, const Lobby& aLobby); // Hinzu friend ostream& operator<<(ostream& os, const Player& p); ... }; // Hinzu ostream& operator<<(ostream& os, const Player& p) { os << p.GetName(); return os; } void Lobby::AddPlayer() { //create a new player node cout << "Please enter the name of the new player: "; string name; cin >> name; Player* pNewPlayer = new Player(name); ... cout << pNewPlayer; // cout << *pNewPlayer; }Bei den letzten Zeilen bekomme ich nun im ersten Fall (28) lediglich die Adresse des Objektes, im zweiten Fall (29) wieder eine Reihe von Fehlermeldungen. Ich hätte gedacht, dass die erste Zeile funktionieren muss, da doch eine Referenz auch nur ein Zeiger auf ein Objekt ist?!
-
Die Funktion muss ein Friend von Player sein, nicht von Lobby. Mit Lobby hat sie nichts am Hut.
-
MFK schrieb:
Die Funktion muss ein Friend von Player sein, nicht von Lobby. Mit Lobby hat sie nichts am Hut.
Oh natürlich! Das fällt nun unter die Rubrik Flüchtigkeitsfehler.

So funktioniert es jetzt, jedoch mit:cout << *pNewPlayer;Klar, dass der Zeigerinhalt die Referenz auf das Objekt darstellt. Aber die ist doch auch nur ein Zeiger, oder? Also wäre der Zeiger pNewPlayer ein Zeiger auf einen Zeiger, richtig?
-
TestGuest schrieb:
Klar, dass der Zeigerinhalt die Referenz auf das Objekt darstellt. Aber die ist doch auch nur ein Zeiger, oder? Also wäre der Zeiger pNewPlayer ein Zeiger auf einen Zeiger, richtig?
Lös dich bitte von der Vorstellung, dass eine Referenz "auch nur ein Zeiger" ist. Es stimmt einfach nicht.
-
MFK schrieb:
Lös dich bitte von der Vorstellung, dass eine Referenz "auch nur ein Zeiger" ist. Es stimmt einfach nicht.
Ok, dann nehme ich das so hin. Kannst Du den Unterschied illustrieren? Weder in dem Buch, noch in Tutorials wird mir richtig klar, wo der Unterschied liegt, bzw. inwieweit beides austauschbar ist (also, kann man z.B. einen Zeiger übergeben, wo eine Referenz verlangt wird, usw.)?!
Ich habe auch Probleme mit den Konventionen von Zeigern. Im Buch steht folgendes:
int score = 100; int* const pScore = &score;ist ein Konstanter Zeiger. Weiterhin
const int* pNumber = &score;ist ein Zeiger auf eine Konstante. Und dann gibt es halt noch konstante Zeiger auf eine Konstante. In einem Tutorial hier aus dem Forum (nämlich aus cpp_mta_rwthaa.pdf) ist:
char const *p:ein Zeiger auf eine Konstante, und:
char w; char * const p = w;ein konstanter Zeiger. Gibt es eine Fausregel, wie man sich die unterschiedlichen Schreibweisen einprägen kann?
-
TestGuest schrieb:
Kannst Du den Unterschied illustrieren? Weder in dem Buch, noch in Tutorials wird mir richtig klar, wo der Unterschied liegt, bzw. inwieweit beides austauschbar ist (also, kann man z.B. einen Zeiger übergeben, wo eine Referenz verlangt wird, usw.)?!
Austauschbar sind also nicht Zeiger und Referenz, sondern Referenz und Objekt. Zeiger sind etwas komplett anderes.
Ein Zeiger ist ein eigenständiges Objekt, das einen Verweis auf ein anderes Objekt speichern kann (aber nicht muss). Auf welches Objekt ein Zeiger verweist, kann man ändern (daher die Unterscheidung zwischen konstantem Zeiger und Zeiger auf Konstante). Ein Zeiger kann Null sein, also auf nichts zeigen. Einen Zeiger behandelst du grundsätzlich anders als das Objekt, auf das es zeigt: Wenn du ihn übergeben willst, wo Objekt oder Referenz verlangt sind, musst du ihn dereferenzieren, um auf die Member zuzugreifen, brauchst du -> statt . usw.
Eine Referenz ist ein neuer Name für ein bestehendes Objekt. Wenn du ein Objekt A hast und eine Referenz B dafür anlegst, sagst du dem Compiler: "Wann immer ich jetzt B sage, meine ich Objekt A". Die Referenz ist also insofern kein eigenständiges Objekt, als du sie nicht beeinflussen kannst. Alles, was du mit der Referenz machst, machst du mit dem Objekt, für das sie steht. Weder kann eine Referenz auf ein anderes Objekt "umgebogen" werden, noch kann sie auf nichts zeigen (es sei denn, die Referenz lebt länger als das Objekt, für das sie steht, aber das ist ein Programmierfehler). Du behandelst eine Referenz im Code genauso wie das eigentliche Objekt.
TestGuest schrieb:
Ich habe auch Probleme mit den Konventionen von Zeigern.
Einfache Faustregel: Das const bezieht sich immer auf das, was links davon steht. Wenn das const ganz links steht, bezieht es sich auf das, was rechts davon steht. Folgende Deklarationen sind also gleichwertig:
const int* p; int const* p;Und ob du vor oder nach dem Stern Leerzeichen machst, ist inhaltlich völlig egal.
-
MFK schrieb:
Austauschbar sind also nicht Zeiger und Referenz, sondern Referenz und Objekt. Zeiger sind etwas komplett anderes.
Ich wollte ein entsprechends Beispiel suchen, habe aber festgestellt, dass ich Referenzoperator (string& obj) und Adressoperator (int* a = &b) verwechselt hatte.
MFK schrieb:
Wenn du ihn übergeben willst, wo Objekt oder Referenz verlangt sind, musst du ihn dereferenzieren, um auf die Member zuzugreifen, brauchst du -> statt . usw.
Das ist klar, oder auch (*pointer).member
MFK schrieb:
Einfache Faustregel: Das const bezieht sich immer auf das, was links davon steht. Wenn das const ganz links steht, bezieht es sich auf das, was rechts davon steht. Folgende Deklarationen sind also gleichwertig:
const int* p; int const* p;Und ob du vor oder nach dem Stern Leerzeichen machst, ist inhaltlich völlig egal.
Folgendes Zeilen sind laut Buch unterschiedlich:
int* const p = &i; const int* p; const int* const p = &i;Im ersten Fall sollt es sich um einen konstanten Zeiger handeln, im zweiten Fall um einen Zeiger auf eine Konstante, letzteres ein konstanter Zeiger auf eine Konstante. Nach Deiner Erklärung würde sich also das const immer auf int* beziehen, im letzten Fall sogar doppelt. Das habe ich nicht verstanden.
PS: Was war denn mit dem Forum los?
-
TestGuest schrieb:
Nach Deiner Erklärung würde sich also das const immer auf int* beziehen, im letzten Fall sogar doppelt. Das habe ich nicht verstanden.
Das const bezieht sich niemals auf int*. Du musst int und * getrennt betrachten. Du kannst das const auch dazwischen schreiben:
int* const p = &i; // const bezieht sich auf * const int* p; // const bezieht sich auf int int const* p; // const bezieht sich auf int const int* const p = &i; // ein const für int, eins für * int const * const p = &i;// ein const für int, eins für *
-
MFK schrieb:
Das const bezieht sich niemals auf int*. Du musst int und * getrennt betrachten. Du kannst das const auch dazwischen schreiben:
int* const p = &i; // const bezieht sich auf * const int* p; // const bezieht sich auf int int const* p; // const bezieht sich auf int const int* const p = &i; // ein const für int, eins für * int const * const p = &i;// ein const für int, eins für *Vielen Dank, das ist so absolut verständlich! Bei der Gelegenheit hab ich mich gerade mal offiziell angemeldet.
Schönen Gruß