Klassen Templates
-
Hallo zusammen,
ein kleines Problemchen zum Verständnis von Templates hab ich hier:
Es gibt zwei beliebige Klasse und eine Funktion, die klassenübergreifend berechnen soll.
Da hab ich nun ein Template gebaut: template <class T> class vierteAber wie greife ich darauf zu??
(Und eine Minifrage befindet sich im Quellcodewarum steht manchmal class T und manchmal typename T, wo ist da ein Unterschied?)
#include <iostream> class erste { public: erste(int pa, double pb, double pc) : a(pa), b(pb), c(pc) {}; int ra() {return a;}; double rb() {return b;}; double rc() {return c;}; private: int a; double b,c; // andere irrelevante Variablen }; class zweite { public: zweite(int pa, char pd, char pe) : a(pa), d(pd), e(pe) {}; int ra() {return a;}; char rd() {return d;}; char re() {return e;}; private: int a; char d,e; // andere irrelevante Variablen }; template <class T> class vierte { vierte(T test1, T test2) {rechnen(test1,test2);}; int b; int rechnen(T test1, T test2) ; }; template <typename T> // was ist der Unterschied zwischen den class/typename? int vierte<T>::rechnen(T test1, T test2) { b=test1.ra() + test2.ra(); return b; } int main() { erste var1(4,5,3); zweite var2(10,'d','f'); template <vierte> test3(var1,var2); // wie bekomme ich da ein Element raus?? ^^ std::cout << test3.rechnen(var1,var2); return 0; }
gnah ... ist bestimmt banal, aber ich seh die Lösung vor lauter Büchern nicht mehr ...
(Stroustrup, Primer, Der C++ Programmierer und Meyers - neueste Auflagen - liegen hier ... unibib sei dank
)
Anm.: ich mach das erst seit 2 Wochen, und das mit Unterbrechung. Also bitte kein Flamewar, danke.Ich bedanke mich im Voraus!
-
was ist der Unterschied zwischen den class/typename?
Es gibt keinen. Beide sind für Template-Parameter semantisch äquivalent.
Und zu deiner Frage, wie du ein Objekt aus einem Klassentemlate erzeugst...
Zuerst musst du über das Klassentemplate eine Klasse erzeugen lassen, mit gewünschten Template-Argumenten. Diese Klasse wird dann Spezialisierung genannt.Also, um auf eine Spezialisierung zu verweisen, nutzt du eine sog. template-id:
vierte<int>
vierte<int>
ist eine Klasse - sie ist eine Spezialisierung vonvierte
.
Damit kannst du dann ein Objekt a lavierte<int> test(var1, var2);
deklarieren.
Nun ist aber das Problem, dass
var1
undvar2
zwei verschiedene Typen haben, und du daher auch zwei Template-Parameter brauchst. Lösung:template <class T1, typename T2> class vierte { vierte(T1 test1, T2 test2) {rechnen(test1,test2);}; int b; int rechnen(T1 test1, T2 test2) ; }; template <typename T1, typename T2> int vierte<T1, T2>::rechnen(T1 test1, T2 test2) { b=test1.ra() + test2.ra(); return b; }
(ungetestet)
Edit: Das Erstellen der Variable dann etwa so
vierte<erste, zweite> test3(var1, var2);
P.S.: Ich bins.
-
Arcoth schrieb:
P.S.: Ich bins.
Danke, Sone!
Klappt wunderbar!Der Vollständigkeitshalber ist der Code nochmal unten (getestet! Ein public hat gefehlt)
was ist der Unterschied zwischen den class/typename?
Es gibt keinen. Beide sind semantisch äquivalent.
ok. Warum wird das mal so mal so verwendet?? Historische Gründe? Lesbarkeit? Steckt da mehr dahinter?
#include <iostream> class erste { public: erste(int pa, double pb, double pc) : a(pa), b(pb), c(pc) {}; int ra() {return a;}; double rb() {return b;}; double rc() {return c;}; private: int a; double b,c; // andere irrelevante Variablen }; class zweite { public: zweite(int pa, char pd, char pe) : a(pa), d(pd), e(pe) {}; int ra() {return a;}; char rd() {return d;}; char re() {return e;}; private: int a; char d,e; // andere irrelevante Variablen }; template <class T1, typename T2> class vierte { public: vierte(T1 test1, T2 test2) {rechnen(test1,test2);}; int b; int rechnen(T1 test1, T2 test2) ; }; template <typename T1, typename T2> int vierte<T1, T2>::rechnen(T1 test1, T2 test2) { b=test1.ra() + test2.ra(); return b; } int main() { erste var1(4,5,3); zweite var2(10,'d','f'); vierte<erste, zweite> test3(var1, var2); // klappt! std::cout << test3.rechnen(var1,var2); return 0; }
-
ok. Warum wird das mal so mal so verwendet?? Historische Gründe? Lesbarkeit? Steckt da mehr dahinter?
Ich habe es ergänzt, es ist natürlich nur für Template-Parameter äquivalent (dachte, das wäre aus dem Kontext klar, aber wenn ich es überdenke...).
Und es gibt eine Ausnahme, aber die ist für dich noch sehr uninteressant, daher habe ich es pauschal gefasst.Soweit ich mich erinnere, hat das seinen Ursprung, da vorerst nur
class
erlaubt war - man nachher jedoch das Schlüsselworttypename
eingeführt hat - und dann hattypename
ja viel besser gepasst, also waren fortan beide erlaubt. Ich finde jedoch gerade nichts dazu, ich glaube, das hat anders begonnen.Außerhalb der Template-Parameter-Deklaration wird aber
class
verwendet, um bspw. eine Klasse zu deklarieren,typename
hat andere Zwecke (die du auch recht bald erfährst).
-
Merci!
-
Ja, ich hatte Recht, hier ist der Artikel der es erklärt: http://blogs.msdn.com/b/slippman/archive/2004/08/11/212768.aspx
-
Jetzt kommen doch noch Fragen
Ich hab da mal einen Vector genommen und will das jetzt füllen. Eigentlich will ich ja dass das Template mir die Aufgabe abnimmt zu gucken, ob ich Elemente von der ersten oder zweiten Klassen zum Berechnen nehme. Aber so wie es jetzt ist, muss ich das jedesmal von Hand selber lösen.
**
FRAGE: Geht das überhaupt mit einem Vector? (Mir kommen da schon verkettete Listen in den Sinn. Aber ich will ja weg von den C Gedanken!)**
#include <iostream> #include <vector> class erste { public: erste(int pa, double pb, double pc) : a(pa), b(pb), c(pc) {}; int ra() {return a;}; double rb() {return b;}; double rc() {return c;}; private: int a; double b,c; // andere irrelevante Variablen }; class zweite { public: zweite(int pa, char pd, char pe) : a(pa), d(pd), e(pe) {}; int ra() {return a;}; char rd() {return d;}; char re() {return e;}; private: int a; char d,e; // andere irrelevante Variablen }; template <class T1, typename T2> class vierte { public: vierte(T1 test1, T2 test2) {rechnen(test1,test2);}; int b; int rechnen(T1 test1, T2 test2) ; }; template <typename T1, typename T2> int vierte<T1, T2>::rechnen(T1 test1, T2 test2) { b=test1.ra() + test2.ra(); return b; } int main() { erste var1(4,5,3); zweite var2(10,'d','f'); zweite var3(15,'d','f'); vierte<erste, zweite> test3(var1, var2); std::cout << test3.rechnen(var1,var2) << "\n"; std::vector<vierte<zweite,zweite> > test1; // und wenn es mal andere Verknüpfungen werden sollen?? test1.push_back(vierte<zweite, zweite>(var3,var2)); std::cout << test1[0].rechnen(var3,var2) << "\n"; return 0; }
Ja, ich hatte Recht, hier ist der Artikel der es erklärt: http://blogs.msdn.com/b/slippman/archive/2004/08/11/212768.aspx
Vielen Dank fürs Recherchieren
-
zuerstmal solltest du dir vielleicht andere dinge anschauen, z.b. das korrekte setzen von semikola oder const-correctness.
zur letzten frage: klar geht das aber wieso machst du es nicht mit polymorphie?
#include <iostream> #include <vector> class base { int a; public: base(int pa) : a(pa) { } virtual ~base() { } int ra() const { return this->a; } }; class erste : public base { double b, c; public: erste(int pa, double pb, double pc) : base(pa), b(pb), c(pc) { } double rb() const { return this->b; } double rc() const { return this->c; } }; class zweite : public base { char d, e; public: zweite(int pa, char pd, char pe) : base(pa), d(pd), e(pe) { } char rd() { return this->d; } char re() { return this->e; } }; struct vierte { vierte(base const& test1, base const& test2) { this->rechnen(test1, test2); } int b; int rechnen(base const& test1, base const& test2) { return this->b = test1.ra() + test2.ra(); } }; int main() { erste var1(4, 5, 3); zweite var2(10, 'd', 'f'); zweite var3(15, 'd', 'f'); vierte test3(var1, var2); std::cout << test3.rechnen(var1, var2) << '\n'; std::vector<vierte> test1; test1.push_back(vierte(var3, var2)); std::cout << test1[0].b << '\n'; }
edit: ich persönlich finde das design von "vierte" einfach nur schlecht, wieso kein funktor / keine funktion?
edit 2:
Lymogry schrieb:
FRAGE: Geht das überhaupt mit einem Vector? (Mir kommen da schon verkettete Listen in den Sinn. Aber ich will ja weg von den C Gedanken!
)
versteh ich nicht, in c++ gibt es auch verkettete listen und in c gibt es auch zusammenhängede speicherfelder. ausserdem, wieso sollte dies mit verketteten listen gehen wenn es mit vektoren nicht geht?
-
Eigentlich will ich ja dass das Template mir die Aufgabe abnimmt zu gucken, ob ich Elemente von der ersten oder zweiten Klassen zum Berechnen nehme.
Das geht so nicht. Der Container kann immer nur Elemente eines bestimmten Typs halten, und
vierte<erste, erste>
undvierte<zweite, erste>
sind zwei verschiedene Klassen und demnach zwei verschiedene Typen.Was wäre jetzt genau die Frage? Ob man Objekte verschiedenen Typs in einen Container einsetzen kann? Nein. Aber bestimmte Klassen können von einer Basisklasse ableiten, und Verweise auf diese kannst du dann im Container speichern. Polymorphie eben.
zuerstmal solltest du dir vielleicht andere dinge anschauen, z.b. das korrekte setzen von semikola oder const-correctness.
Unsinn, das ist beides in Ordnung.
-
Arcoth schrieb:
zuerstmal solltest du dir vielleicht andere dinge anschauen, z.b. das korrekte setzen von semikola oder const-correctness.
Unsinn, das ist beides in Ordnung.
Lymogry schrieb:
int ra() {return a;}; double rb() {return b;}; double rc() {return c;};
-
igno schrieb:
Lymogry schrieb:
int ra() {return a;}; double rb() {return b;}; double rc() {return c;};
Stört dich irgendetwas davon in diesem Beispiel? Das sind Dinge die der TE noch lernt, und die hier - in einem Thread über Klassentemplates - einfach irrelevant sind.
Hast du irgendein gutes Tutorial zum Semikola-Setzen im Internet gefunden? Lass es mich wissen.
-
Arcoth schrieb:
Stört dich irgendetwas davon in diesem Beispiel?
dass es störend sei, davon war nie die rede...
Arcoth schrieb:
Das sind Dinge die der TE noch lernt,
ich bin der meinung, dass man klassen lernt bevor man klassen-templates lernt...
Arcoth schrieb:
die hier - in einem Thread über Klassentemplates - einfach irrelevant sind.
wenn der te es hier nicht richtig macht (in so einem kurzen listing wo alles übersichtlich ist), muss man davon ausgehen, dass er es auch in einem grösseren vorhaben nicht richtig machen würde, daher hab ich die stichworte in den raum geworfen. wenn er es schon kann, dann kann er meinen tipp getrost ignorieren. wenn er es nicht kann, hat er nun anhaltspunkte, was er noch lernen muss / sollte. was ist daran falsch?
Arcoth schrieb:
Hast du irgendein gutes Tutorial zum Semikola-Setzen im Internet gefunden? Lass es mich wissen.
du hast c++ mit internet-tutorials gelernt?
-
Arcoth schrieb:
Das sind Dinge die der TE noch lernt,
ich bin der meinung, dass man klassen lernt bevor man klassen-templates lernt...
Der Meinung bin ich auch.
Dass der TE jedoch ein Grundlagenbuch durcharbeitet, weiß ich. Wenn er sich vorher in andere Gebiete reinwagt, ist das eigentlich löblich. Memberfunktionen, die auch konstante Objekte dieser Klasse aufrufen können, lernt er noch kennen.
wenn er es nicht kann, hat er nun anhaltspunkte, was er noch lernen muss / sollte.
was ist daran falsch?Du erklärst gar nichts. Du wirfst nur irgendwelche Begriffe in den Raum, von denen der TE nicht einmal die Verbindung zu seinem Beispiel kennt. Wenn du genau erklärt hättest, was wohin gehört bzw. nicht gehört, und wieso, wäre es hilfreich. So nützt es niemandem.
du hast c++ mit internet-tutorials gelernt?
Nein, aber in meinem Stroustrup gibt es ebenfalls kein Kapitel zur Semikola-Setzung.
-
Arcoth schrieb:
Eigentlich will ich ja dass das Template mir die Aufgabe abnimmt zu gucken, ob ich Elemente von der ersten oder zweiten Klassen zum Berechnen nehme.
Das geht so nicht. Der Container kann immer nur Elemente eines bestimmten Typs halten, und
vierte<erste, erste>
undvierte<zweite, erste>
sind zwei verschiedene Klassen und demnach zwei verschiedene Typen.Was wäre jetzt genau die Frage? Ob man Objekte verschiedenen Typs in einen Container einsetzen kann? Nein. Aber bestimmte Klassen können von einer Basisklasse ableiten, und Verweise auf diese kannst du dann im Container speichern. Polymorphie eben.
Danke!! Genau das war die Frage.
Es geht also gar nicht. Gut zu wissen.ingo schrieb:
zur letzten frage: klar geht das aber wieso machst du es nicht mit polymorphie?
Ich lerne.
Danke für das Beispiel!!!ingo schrieb:
Arcoth schrieb:
zuerstmal solltest du dir vielleicht andere dinge anschauen, z.b. das korrekte setzen von semikola
Unsinn, das ist beides in Ordnung.
Lymogry schrieb:
int ra() {return a;}; double rb() {return b;}; double rc() {return c;};
ähm ............
Ich dachte, das muss so. Weil es ja eigentlich mit einer Inline Funktion auch ausgelagert werden könnte und dann würden die Semikola auch da stehen. Bei normalen Prozeduren mach ich sowas auch nie (ich kann eigentlich gut C), aber bei den Klassen dachte ich, dass das so da stehen muss, und weil auch der Compiler da kein bisschen meckert...
ok. ich mach sie wegigno schrieb:
zuerstmal solltest du dir vielleicht andere dinge anschauen,... const-correctness.
Siehe Einganspost:
(Stroustrup, Primer, Der C++ Programmierer und Meyers - neueste Auflagen - liegen hier ... unibib sei dank)
Anm.: ich mach das erst seit 2 Wochen, und das mit Unterbrechung. Also bitte kein Flamewar, danke.
ich lerne, .....
Aber was meinst du mit const-correctness? Unter dem Stichpunkt find ich jetzt nichts in den Büchern ...UND JETZT NOCH INHALTLICHE FRAGEN
Arcoth schrieb:
Memberfunktionen, die auch konstante Objekte dieser Klasse aufrufen können, lernt er noch kennen.
Meine Variable a ist aber nicht konstant. Es ist nur irgendeine Integerzahl, die es in beiden Klassen gibt, sie könnte ja auch anders heissen. Bzw könnten beide Klassen gaaaanz unterschiedlich sein, sagen wir ein Auto und eine Birne. Wird die abstrakte Klasse, von der beide erben, dann "Objekte des realen Lebens"?
Warum ist das a im Beispiel von ingo const ??
Arcoth schrieb:
Dass der TE jedoch ein Grundlagenbuch durcharbeitet, weiß ich. Wenn er sich vorher in andere Gebiete reinwagt, ist das eigentlich löblich.
Ja, sie springt gerne von Thema zu Thema,
um auszuprobieren und rumzuspielen. Viele Fragen ergeben sich erst wenn man sich intensiver als nur mit den Beispielen aus dem Buch auseinandersetzt. Und einiges wird dann Murks oder auch nicht, aus Fehlern lernt man. (Anm.: ich bin grad bei Templates gelandet, weil ich gerade mit dem Überladen von Funktionen rumgespielt habe und dann direkt zu den Templates gekommen bin.
Eigentlich bin ich grad im Kapitel mit Vererbung. Aber rumprobieren schadet ja nicht.
)
Das Forum ist top!
-
Lymogry schrieb:
ingo schrieb:
Arcoth schrieb:
zuerstmal solltest du dir vielleicht andere dinge anschauen, z.b. das korrekte setzen von semikola
Unsinn, das ist beides in Ordnung.
Lymogry schrieb:
int ra() {return a;}; double rb() {return b;}; double rc() {return c;};
ähm ............
Ich dachte, das muss so. Weil es ja eigentlich mit einer Inline Funktion auch ausgelagert werden könnte und dann würden die Semikola auch da stehen. Bei normalen Prozeduren mach ich sowas auch nie (ich kann eigentlich gut C), aber bei den Klassen dachte ich, dass das so da stehen muss, und weil auch der Compiler da kein bisschen meckert...
ok. ich mach sie weges gilt einfach allgemein, dass ein semikolon nach der funktions-deklaration nötig ist, nicht aber nach der funktions-definition. ist sowohl in c, als auch in c++ so (damit ist sowohl inner- als auch ausserhalb der klassen gemeint).
Lymogry schrieb:
igno schrieb:
zuerstmal solltest du dir vielleicht andere dinge anschauen,... const-correctness.
Siehe Einganspost:
(Stroustrup, Primer, Der C++ Programmierer und Meyers - neueste Auflagen - liegen hier ... unibib sei dank)
Anm.: ich mach das erst seit 2 Wochen, und das mit Unterbrechung. Also bitte kein Flamewar, danke.
ich lerne, .....
Aber was meinst du mit const-correctness? Unter dem Stichpunkt find ich jetzt nichts in den Büchern ...Lymogry schrieb:
UND JETZT NOCH INHALTLICHE FRAGEN
Arcoth schrieb:
Memberfunktionen, die auch konstante Objekte dieser Klasse aufrufen können, lernt er noch kennen.
Meine Variable a ist aber nicht konstant. Es ist nur irgendeine Integerzahl, die es in beiden Klassen gibt, sie könnte ja auch anders heissen. Bzw könnten beide Klassen gaaaanz unterschiedlich sein, sagen wir ein Auto und eine Birne. Wird die abstrakte Klasse, von der beide erben, dann "Objekte des realen Lebens"?
Warum ist das a im Beispiel von ingo const ??
die variable a ist - je nach dem ob die instanz der klasse konstant ist - konstant oder nicht (da du bei der definition von a weder explizit const noch mutable nutzt).
meine basis-klasse ist btw nicht abstrakt, denn abstrakte klassen definieren sich dadurch, mindestens eine rein virtuelle funktion zu haben, ist aber nicht gegeben. ausserdem, solange die beiden erbenden klassen etwas gemeinsam haben (in diesem fall ist es die tatsache, dass man sie miteinander addieren kann), ist es nicht falsch, sie von der selben basis ableiten zu lassen. nur wirst du im echten leben kaum eine birne mit einem auto addieren können...
ausserdem ist mein "int a;" keinesfalls konstant, die getter-funktion dazu benötigt nur einen konstanten this-zeiger (wozu auch mehr, es wird eh nur eine kopie der instanz erzeugt). das ist das, was ich vorhin mit const-correctness angesprochen habe.
-
Meine Variable a ist aber nicht konstant.
Das ist hier völlig unwichtig.
Wenn du eine Memberfunktion mit
const
qualifizierst, dann heißt das, dass auch konstante Objekte diese aufrufen können.struct A { void foo() { std::cout << "foo()\n"; } void bar() { std::cout << "foo()\n"; } void foo() const { std::cout << "foo() const\n"; } }; int main() { A a; A const b; a.foo(); b.foo(); // b.bar(); Compilerfehler - bar() kann von einem konstanten Objekt nicht aufgerufen werden }
Ich dachte, das muss so. Weil es ja eigentlich mit einer Inline Funktion auch ausgelagert werden könnte und dann würden die Semikola auch da stehen.
Nein, würden sie nicht. Nach einer Funktionsdefinition muss nie ein Semikolon stehen. Nur bei einer Deklaration:
int ra();
Das ist doch in C genauso.
Edit: Da kam mir ingo zuvor.Bzw könnten beide Klassen gaaaanz unterschiedlich sein, sagen wir ein Auto und eine Birne. Wird die abstrakte Klasse, von der beide erben, dann "Objekte des realen Lebens"?
Es kommt völlig auf das Design an. Ich sehe übrigens nicht, inwiefern man hier eine Basisklasse benötigt, denn beide Klassen haben möglicherweise keine Funktionalität gemein. Wenn sie tatsächlich ein Konzept teilen, welches man aus ihnen herausabstrahieren könnte, dann wäre das sinnvoll.
-
mein name ist igno, ingo ist ein anderer nutzer.
-
igno schrieb:
meine basis-klasse ist btw nicht abstrakt, denn abstrakte klassen definieren sich dadurch, mindestens eine rein virtuelle funktion zu haben, ist aber nicht gegeben. ausserdem, solange die beiden erbenden klassen etwas gemeinsam haben (in diesem fall ist es die tatsache, dass man sie miteinander addieren kann), ist es nicht falsch, sie von der selben basis ableiten zu lassen.
Nachdem ich jetzt eindrücklich gelernt habe, nur von Klassen abzuleiten, mit denen es eine IST-EIN Beziehung gibt, bin ich jetzt leicht verwirrt .... Die beiden Klassen HABEN doch nur etwas gemeinsam, dass sie nämlich beide eine integer Zahl besitzen...
Arcoth schrieb:
Ich sehe übrigens nicht, inwiefern man hier eine Basisklasse benötigt, denn beide Klassen haben möglicherweise keine Funktionalität gemein. Wenn sie tatsächlich ein Konzept teilen, welches man aus ihnen herausabstrahieren könnte, dann wäre das sinnvoll.
ja, ich sehs auch nicht ... aber ich bin ja auch verwirrd und unwissend
-
beide klassen haben ein gemeinsames member und eine gemeinsame methode. ausserdem wird ein operator in form einer klasse angeboten, der als argument je eine der beiden klassen akzeptiert. klar, es besteht auch die möglichkeit diesen getter 2x zu schreiben, sowie den operator 4x...
über sinnvolle vererbung lässt sich nicht streiten solange die klassen nichts repräsentieren sondern einfach nur zum test da sind.
-
Die beiden Klassen HABEN doch nur etwas gemeinsam, dass sie nämlich beide eine integer Zahl besitzen..
Wir wissen nichts über die Bedeutung dieser Zahl, da du sie unwillkürlich hinzugefügt hast. Vielleicht brauchen ja nur zufällig beide einen
int
als Member.Wen sie hingegen beide bspw. eine Art ID brauchen, also beide einen
int
aus demselben Grund, dann sollten sie von einer Basisklasse ableiten, die ein Objekt mit einer ID repräsentiert, und so diese Funktionalität auslagern.Daher:
über sinnvolle vererbung lässt sich nicht streiten solange die klassen nichts repräsentieren sondern einfach nur zum test da sind.