Operatorüberladung in Templateklassen
-
phw schrieb:
Geht auch ausserhalb der klasse (als friend), musst nur aufpassen, dass du die template-parameter nicht überschreibst:
#include <iostream> using namespace std; template<class T> class myTemp { T val; public: template<class Tp> //schau: Tp statt T! friend void operator+= (myTemp<Tp>& lhs, myTemp<Tp>& rhs); /*ist der richtig so? bin mir irgendwie unsicher...*/ myTemp(T arg):val(arg){} T rval(){return val;} }; template<class T> void operator+= (myTemp<T>& lhs, myTemp<T>& rhs) { lhs.val+=rhs.val; } int main() { myTemp<double> a(235.92),b(248.5); a+=b; cout<<a.rval()<<endl; }was genau bedeutet das?
(Fang grad erst mit Templates an)public: template<class Tp> //schau: Tp statt T!
-
Das hier ein anderer Typ benutzt werden kann als in der Klasse benutzt wird.
template< typename T > struct Test { T ein_typ; template< typename U > void Print( U einanderertyp ) { std::cout << einanderertyp << std::endl; }; void Print(){ this->Print(this->ein_typ); // Benutzt das interne Print für den eigenen Typ T } }; int main() { Test<int> tobj; // Wir benutzen für den Typ der Klasse int tobj.ein_typ = 123; // hier weisen wir ein integer dem member zu tobj.Print(); // schreibt den member tobj.Print("Und nun schreiben wir einen String"); tobj.Print(21.2322f); // Und hier ein double }Wie du siehst kannst du verschiedene Typen für die Methode Print() nehmen.
Ich hab in meinem Beispiel ein U genommen weil das nicht ganz so verwirrt, im grunde genommen ist es egal welchen Namen du dafür benutzt so lange er nicht schon vergeben ist, und darunter fällt auch z.b. das T, solltest du das als namen benutzen wird der Compiler dir das um die Ohren hauen
Wenn du Print jetzt ausserhalb definieren möchtest geht das folgendermaßen:
template< typename T > struct Test { T ein_typ; template< typename U > void Print( U einanderertyp ); void Print(){ this->Print(this->ein_typ); } }; template< typename T > template< typename U > void Test<T>::Print( U einanderertyp ) { std::cout << einanderertyp << std::endl; }BR
-
hm...ist schon relativ kompliziert, muss ich mir mal noch bissl anschauen!
-
template<class T> class myTemp { T val; public: template<class Tp> //schau: Tp statt T! friend void operator+= (myTemp<Tp>& lhs, myTemp<Tp>& rhs); /*ist der richtig so? bin mir irgendwie unsicher...*/ myTemp(T arg):val(arg){} T rval(){return val;} };In dem Fall bezieht sich das template<class T> auf den kompletten Block danach, nämlich die gesamte Klassendefinition.
Das template<class tp> bezieht sich auch auf den Block, der danach folgt. Das ist in diesem Fall nur die friend-deklaration. Das heißt, dass jeder operator der Form void operator+=(myTemp<irgendeintyp>& lhs, myTemp<irgendeintyp>& rhs) ein Freund von beispielsweise myTemp<int> ist.
Das ist in den meisten Fällen natürlich fragwürdig, denn warum sollte z.B. ein operator+=, der auf myTempstd::string 's arbeitet, Zugriff auf myTemp<int> 's bekommen?
Grundsätzlich dürfte das ganze ohne die zweite Templatisierung reichen:friend void operator+=(myTemp<T>& lhs, myTemp<T>& rhs);so hat der Operator nur Zugriff auf die KLassenelemente "seines" Typs.
(Anm: wie schon oben dargelegt entspricht die void-Deklaration des Operators nicht ganz den Konventionen, des weiteren ist die Klassenexterne Deklaration unnötig und bricht zusammen mit friend auch noch die Regeln der Kapselung (und damit der OOP))
-
pumuckl schrieb:
...und bricht zusammen mit friend auch noch die Regeln der Kapselung (und damit der OOP))
Humbug, wo hast du das denn her?
Das der Operator nicht ok ist, das stimmt aber was du da über die Kapselung erzählst ist quark.
Nur weil du einen friend deklarierst brichst du noch lange nicht das Prinzip
-
friends stärken die Kapselung sogar.
-
Man kann sich durchaus drüber streiten:
http://www.fh-wels.at/skripten/Derndorfer/WWW_Website/Lehre/Cpp_Vorlesung/Friend_Funktionen_und_Friend_Klassen.html
http://www.schornboeck.net/ckurs/friends.htm
http://www4.in.tum.de/~pretschn/teaching/testsem02-ausarb/kortkamp-ootest.pdf Absatz 3.1 -> friend durchbricht die Kapselunghttp://your.orf.at/teletext/cbarchiv/cpp124.txt -> "... was dem Prinzip der Datenkapselung in gewissem Sinne widerspricht. Friend-Funktionen können als indirektes Mitglied betrachtet werden"
Google hat eine einzige Quelle (und zig Kopien davon) gefunden, in der davon gesprochen wird, dass friend die Kapselung nicht verletzt:
z.B. http://www.lexikon-definition.de/Objektorientierte-Programmierung.htmlnun ist es aber so, dass man in den einschlägigen allgemeinen (nicht C++-bezogenen) Werken über OOP nachlesen kann, dass zur Kapselung unter anderem gehört, dass Objekte nach außen eine einheitliche Schnittstelle bieten, und das macht eine friend-Deklaration nunmal zunichte, da sie bestimmten Klassen/Funktionen eine erweiterte Schnittstelle bietet.
-
In den letzten Wochen haben Sie das
Überladen von Operatoren und Friend-
Funktionen kennengelernt.
Kurz zusammengefasst: eine Friend-Funk-
tion ist nicht Mitglied einer Klasse,
darf aber auf deren private Elemente
zugreifen, was dem Prinzip der Daten-
kapselung in gewissem Sinne wider-
spricht.
Andererseits muss die Erlaubnis für
eine Funktion, friend einer Klasse zu
sein, in der Klassendeklaration stehen,
sodass man Friend-Funktionen auch als
indirekte Mitglieder der Klasse be-
trachten kann.Das trifft die Sache ziemlich gut. Die Tatsache das man sich bereits vorhandene Funktionen zu nutze macht, mag vielleicht für OO nicht Ordnung sein, aber es spricht doch sehr für Wiederverwedung von Code, der dannn damit auch nicht zigmal geschrieben werden muss. Natürlich könnte man auch eine Methode implementieren und in dieser einfach diese Funktion Wrappern. Nur kommt dann so etwas wie std::string heraus. Ein Klasse die viel zu fett und überladen ist, was sie gar nicht sein müsste.
BR
//Edit: Zitat aus http://your.orf.at/teletext/cbarchiv/cpp124.txt
//Edit: Ach ja und was diese Kopie angeht die man überall findet. Die ist aus Wikipedia. Was dann auch nicht weiter verwundern sollte
-
evilissimo schrieb:
Die Tatsache das man sich bereits vorhandene Funktionen zu nutze macht, mag vielleicht für OO nicht Ordnung sein, aber es spricht doch sehr für Wiederverwedung von Code, der dannn damit auch nicht zigmal geschrieben werden muss.
Welche bereits vorhandene Funktion?
Wenn eine (als friend deklarierte) Funktion auf die privaten Member einer Klasse zugreift, muss sie Wissen über die Interna der Klasse haben. Das heißt, sie kann nicht vor der Klasse erstellt worden sein, insofern gibts keinen Code, der wiederverwendet werden könnte.
Und eine Funktion, die eigentlich auf Datenmember zugreifen müsste, als Wrapper einer Methode zu schreiben führt nicht dazu, dass die Klasse übergroß wird, sondern nur dazu, dass Tatsachen eingestanen werden, nämlich dass die Klassenschnittstelle tatsächlich so groß ist.
Denn eigentlich ist jede als friend deklarierte Funktion Teil der Klassenschnittstelle. Man könnte schließlich auch jede einzelne Methode (außer vll. Ctor und Dtor) als friend-Funktion implementieren und dann behaupten, man habe eine schmale Klassenschnittstelle - das wäre nur Augenwischerei.@Mods: evtl sollte man diese Diskussion abkapseln und in einen eigenen thread packen? (hat wenig mit op-überladung in templates zu tun...)
-
Na gut das mit der Wiederverwendbarkeit ist ein schlechtes Beispiel aber es wäre durch aus möglich, aber du hast recht( so hab ich gar nicht darüber nach gedacht )
Es macht eigentlich vom Interface auch keinen Unterschied mehr ob eine Funktion friend ist oder Gewrappert wird.BR