friend oder non-friend für X::Print(...) und operator<<
-
In http://www.c-plusplus.net/forum/viewtopic.php?t=79400&postdays=0&postorder=asc&start=0 wurde die Frage, ob es besser sei, op<< als friend oder non-friend festzulegen, kontrovers diskutiert. Was ist richtig?
//... protected: ostream& Print( ostream& os ) const { return os << "(" << real_ << "," << imaginary_ << ")"; } private: double real_, imaginary_; friend ostream& operator<<( ostream& os, const Complex& c ); }; ostream& operator<<( ostream& os, const Complex& c ) // friend { return c.Print(os); }
oder
//... public: ostream& Print( ostream& os ) const { return os << "(" << real_ << "," << imaginary_ << ")"; } private: double real_, imaginary_; }; ostream& operator<<( ostream& os, const Complex& c ) // non-friend { return c.Print(os); }
Herb Sutter hat sich im Buch für non-friend op<< und public Print(...) entschieden.
( Anmerkung: In http://www.gotw.ca/gotw/004.htm findet man einen merkwürdigen Zwitter mit op<< als friend und Print(...) dennoch public. ?? )
Dies spricht allerdings dagegen:
http://www.parashift.com/c++-faq-lite/input-output.html#faq-15.9Scott Meyers Regeln lassen beide Varianten zu:
http://www.cuj.com/documents/s=8042/cuj0002meyers/Wie würden Sie entscheiden?
oder
oder
-
In diesem Fall auf jeden Fall gegen friend. Grund: Eine Complex-Klasse braucht sowieso ein getImag() und ein getReal(). Und damit gibt es keine Notwendigkeit mehr, den << sich zum Freund zu machen.
Der Punkt ist: Es ist nicht notwendig!
Scott Meyers schreibt übrigens auch, dass friend nur dann sinnvoll ist, wenn der operator Informationen braucht, die er nicht über die public-Schnittstelle kriegen kann.
-
Was ist richtig?
Es gibt hier kein Richtig oder Falsch. Es gibt hier nur ein sinnvoll und ein weniger sinnvoll.
Ich gehe mal davon aus, dass du die Deklaration deines op<< immer schön in den Header packst, in dem auch die entsprechende Klasse definiert wird (und natürlich in den selben Namespace). Die Definition gehört in die cpp-Datei in der die Methoden der Klasse definiert werden.
In diesem Fall hast du, nach dem Interface-Prinzip, durch das friend keinen kapselungstechnischen Nachteil (friend-Beziehung werden eigentlich erst bei Long-Distance-Friendships problematisch). Und auch aus physischer Sicht kommt es dadurch nicht zu größeren Abhängigkeiten.Wenn deine Klasse nun teil einer Klassenhierarchie ist, dann macht ein nonfriend op<< + eine virtuelle print-Methode in der Basisklasse deutlich mehr sinn. Eine friend-Deklaration ist in diesem Fall *nicht* nötig (sie ist aber auch nicht gefährtlich). Hast du hinegegen eine konkrete Klasse, die nicht teil einer Hierarchie ist, dann gibt es keinen technischen Grund für eine zusätzliche öffentliche print-Methode (diese Funktion übernimmt ja genau der operator<<). Hast du bereits ein Interface, dass für die Ausgabe ausreichend ist (bei einer sinnvollen Kapselung eher unwahrscheinlich), dann kannst du dieses natürlich auch gleich im op<< benutzen (kein friend). Ich würde aber keinesfalls extra deshalb ein Interface einführen (-> Stichwort: Minimales Interface).
Direkter Zugriff (friend) kann außerdem auch aus Performance-Gründen sinnvoll sein. Allerdings ist das in diesem Zusammenhang eher selten. Hier gilt wie immer: Messen, nicht raten.
-
HumeSikkins schrieb:
Ich gehe mal davon aus, dass du die Deklaration deines op<< immer schön in den Header packst, in dem auch die entsprechende Klasse definiert wird (und natürlich in den selben Namespace). Die Definition gehört in die cpp-Datei in der die Methoden der Klasse definiert werden.
würde ich nicht vorschlagen.
würde eher vorschlagen, daß der op<< als triviale non-friend-inline-funktion und als teil der schnittstelle von Complex direkt unter der klasse Complex steht.edit: aber sauber antworten kann man auf die frage gar nicht.
wegen
protected:
ist anzunehmen, daß Complex eine Baisklasse ist oder werden wird. also mit virtuellem dtor.wegen
ostream& Print( ostream& os ) const
(kein virtual davor!) ist anzunehmen, daß Print bereits in einer basisklasse virtuel gemacht wurde.
der rückgabetyp von Print (nota bene: void printAt(ostream&) ist normal) verwirrt.
der friend hat in der private-section nix verloren. er ist teil der öffentlichen schnittstelle.
und jetzt mal gucken, wie eh es tut, Complex mitten in eine solche Vererbungshierarchie einzubasteln. sehr weh. das ist kein c++, sondern java. daher läßt sich die frage in diesem forum nicht beantworten.
der op<< ist in der basisklasse zu erwarten und hier unsinnig.das ist das problem bei konstruierten beispielen. wird ein beispiel konstruiert, das es so nicht geben kann oder darf, kommen auch nur irrelevante antworten heraus.
-
volkard schrieb:
HumeSikkins schrieb:
Ich gehe mal davon aus, dass du die Deklaration deines op<< immer schön in den Header packst, in dem auch die entsprechende Klasse definiert wird (und natürlich in den selben Namespace). Die Definition gehört in die cpp-Datei in der die Methoden der Klasse definiert werden.
würde ich nicht vorschlagen.
würde eher vorschlagen, daß der op<< als triviale non-friend-inline-funktion und als teil der schnittstelle von Complex direkt unter der klasse Complex steht.Ob inline im Header oder nicht inline, dafür aber innerhalb der cpp-Datei der Klasse spielt in dem Punkt auf den ich hinaus wollte keine Rolle. In beiden Fällen ist sichergestellt, dass der op<< a) zum Interface der Klasse und b) die Implementation des op<< zur Implementation der Klasse gehört. Ersteres ist aus logischer Sicht, letzteres vorallem aus physischer Sicht wichtig (keine zusätzlichen Abhängigkeiten).