Basisklasse als Templateparameter übergeben (sinnvoll???)
-
Also es handelt sich um ein Programm, dass einem als Spielleiter bei einem Rollenspiel (erstmal D&D) das Auswürfeln und -rechnen von NSC (Nicht-Spieler-Charakteren) abnehmen soll.
Keine Ahnung, ob Du damit was anfangen kannst, aber soweit zum Hintergrund des Ganzen.Ich habe es mit halt so gedacht, dass man zu Beginn einige grundlegende Angaben zum gewünschten Charakter macht (Volk, Alter, Geschlecht, Beruf, Machtstufe, evtl. auch sozialer Stand) und das Pogramm dann das Ermitteln der spieltechnischen Werte übernimmt.
Man macht also zu Anfang diese Angaben, z.B. , dass man einen Zwergen Krieger der Stufe 3 oder so haben möchte.
Das Programm würde dann in diesem Fall die Klasse "Krieger" an den Charakter vererben.
( etwa: Klasse<Krieger> Char1 o.ä.)
Daneben erhält er dann noch Ausrüstung u.ä. und einige Veränderung aufgrund seines Volkes.Vielen Dank für Deine Hilfe.
-
Das hat meiner Meinung nach noch gar nichts so mit Templates zu tun, das über Templates zu regeln wäre ziemlicher overhead oder sehe ich das falsch?
Ich denke mit normaler vererbung kommst du dort noch am einfachsten weiter.
class npc { int hp; int mp int str; . . . virtual void berecheWerte() = 0; }; class Krieger : public npc { void berechneWerte() { //..do some calculation } }; class Magier : public npc { void berechneWerte() { //.. do some other calculation } };Ich sehe hier noch gar keinen so wirklichen sinn gleich Templates an zu setzen.Ggf kannst du das berechne Werte nochmal virtualisieren um das ganze für verschiedene Spielsystem nochmals zu überladen.
-
Kurz: Es kann sinnvoll sein
Bei dir würde ich das nicht als sinnvoll erachten, weil du kein festes Interface fedinierst und wenn neue Klassen hinzukommen, kann es sein, dass diese eine Methode nicht definieren. Das führt dann allerdings zu nicht gerade durchsichtigen Fehlermeldungen.Meine letzte Anwendung dafür waren Iteratoren, die sich nur darin unterscheiden, dass der eine konstant war und der andere nicht. Der Rest des Algorithmus war gleich. Daher habe ich das dann einfach folgendermaßen gehandhabt:
template <typename T> struct unconst_btree_iterator { typedef T value_type; typedef T* pointer; typedef T& reference; typedef btree_node<T>* node_link_type; }; template <typename T> struct const_btree_iterator { typedef T value_type; typedef const T* pointer; typedef const T& reference; typedef const btree_node<T>* node_link_type; }; template < typename T, template <class> class constness = unconst_btree_iterator > struct btree_iterator { typedef typename constness<T>::value_type value_type; typedef typename constness<T>::pointer pointer; typedef typename constness<T>::reference reference; //Inhalt der Klasse... }; template <typename T> class Binarytree public: typedef btree_iterator<T, unconst_btree_iterator> iterator; typedef btree_iterator<T, const_btree_iterator> const_iterator; //... };Damit muss ich die Algorithmen nur einmal schreiben

Edit: Mh, irgendwie war ich gerade geistig umnachtet. Das war natürlich ein völlig falsches Beispiel ....
Besseres Beispiel aus der Loki-Bibliothek von Alexandrescu: die Threading-Policy.
Dort wird von der Klasse, die eine Threadingvariante repräsentiert geebrt. Dabei wird eine eingebettete Klasse Lock übernommen:template <class Host> class SingleThreaded { public: struct Lock { Lock() {} Lock(const Host&) {} }; }; template <class Host> class ObjectLevelLockable { public: class Lock; friend class Lock; class Lock { //diese Klasse tut nun tatsächlich etwas bei der Konstruktion }; }; //usw. //Nutzung: template < template <class T> class Threading > class Foo : public Threading<class T> { doSome() { Lock lock; //etwas machen } };
-
Ok, danke.
Dann werde ich es wohl ohne Templates machen.
Kann mir trotzdem noch jemand sagen, warum der Linker bei ein und demselben Code manchmal arbeitet und manchmal abschmiert (undefined reference to...)?
Edit:
Hmmm... obwohl...
Wenn ich, nehmen wir mal an, sechs verschiedene Klassen und sechs verschiedene Völker habe, dann muss ich von jeweils einem Vertreter beider Sparten die Merkmale zusammenfügen.
Und da könnte ich das doch mit Templates machen, oder?Sonst würde ich ja nie fertig:
class Charakterklasse {...}; class Magier : public Charakterklasse {...}; class Kaempfer : public Charakterklasse {...}; class Schurke : public Charakterklasse {...}; (...) class Volk {...}; class Mensch : public Volk {...); class Zwerg : public Volk {...); class Ork : public Volk {...); (...)Und dann bräuchte ich für jede Kombination wieder eine eigene Klasse, oder nicht?
Wobei Ausrüstung etc. ja noch hinzukommt.class MenschKrieger: public Mensch, public Kaempfer {...}; class MenschMagier: public Mensch, public Magier {...};...und so weiter...
Mit Templates könnte man das doch so etwa lösen, oder?
template<class Klasse, class Volk> class NSC : public Klasse, public Volk {... blabla alles hübsch kombinieren...};Oder sehe ich das jetzt falsch?
Edit2: Ach ja, und dann wäre da noch dieser seltsame Linker-Fehler (s.o.) Kann den jemand erklären?
-
Ja, das ist völlig in Ordnung und auch gut. Nennt sich policy-basierters Klassendesign.

-
Ok, danke.
Und kannst du was mit dem Linker-Error anfangen?
"undefined reference to" und dann folgt der Konstruktor meiner Klasse, also z.B.
Klasse<Kaempfer>::Klasse()Aber halt nur manchmal.
Deswegen steige ich da auch nicht durch...
Edit:
Ich füge nochmal den Code an.
Aber an dem kann's ja eigentlich nicht liegen, wenn's mal geht und mal nicht...// klasse.h #include <iostream> #include <string> #include "char.h" #include "kaempfer.h" #ifndef _KLASSE_H_ #define _KLASSE_H_ template <class T> class Klasse : public T, public Char { public: Klasse(); }; #endif// klasse.cpp #include <iostream> #include "klasse.h" #include "char.h" template <class T> Klasse<T>::Klasse():Kaempfer() { name = "Sinthoras"; iTW = 1; };// main.cpp (...) Klasse<Kaempfer> NSC; (...)Manchmal klappt es halt...
Eben auch als ich die Zeile "Klasse<Kaempfer> NSC;" auskommentiert habe (es scheint also wirklich am Konstruktor zu liegen) und seltsamerweise auch, als ich "klasse.cpp" (mit dem Konstruktor) als Test per #include in main eingebunden habe. Soll man ja nich machen.Edit2: Also irgendwie habe ich das Gefühl, er peilt nicht, dass die Konstruktor zu Klasse nicht in klasse.h sondern in klasse.cpp ist.
Aber die Dateien sind hübsch alle in einem Projekt und manchmal gehts ja irgendwie...
-
Bei templates müssen Funktionsprototyp und Implementation in der selben Einheit sein.
// klasse.h #include <iostream> #include <string> #include "char.h" #include "kaempfer.h" #ifndef _KLASSE_H_ #define _KLASSE_H_ // template <class T> class Klasse : public T, public Char { public: Klasse(); }; #include "klasse.cpp" //hier so #endif// klasse.cpp #include <iostream> //#include "klasse.h" und das weg #include "char.h" template <class T> Klasse<T>::Klasse():Kaempfer() { name = "Sinthoras"; iTW = 1; };oder gleich alles in den header
-
Ok, danke, vielen Dank, euch allen.
Edit:
Hm, wenn ich es so einbinde, bekomme ich dann folgende Meldungen:expected constructor, destructor, or type conversion before '<' token
expected `;' before '<' tokenbeides bezogen auf die Zeile:
Klasse<T>::Klasse():Kaempfer()
Wenn ich es direkt im Header mache, kommt wieder "undefinde reference to..".
-
struct A { }; struct Char { std::string name; int iTW; }; template <class T> class Klasse : public T, public Char { public: Klasse(); }; template <class T> Klasse<T>::Klasse() { name = "Sinthoras"; iTW = 1; };Schluckt mein Compiler :o
-
Z.Z. übersetzt er beide Versionen (also auch die falsche), wenn er lustig ist.
Wenn gerade lieber abstürzt, macht er auch das...


-
So mir war grad langweilig, hab dir hier nen Vorschlag. Schau mal ob er dir gefällt.
// Getestet, Pseudofunktionen als Bildschirmausgabe als Kontrolle obs geht #include <iostream> using namespace std; // Charakter Typen class Magier{ public: static void angreifen(){ cout << "zaubere();" << endl; } }; class Kaempfer{ public: static void angreifen(){ cout << "kaempfe();" << endl; } }; class Schurke{ public: static void angreifen(){ cout << "tu_schurkiges();" << endl; // naja Namen sind so ne Sache ;) } }; // Volktypen class Mensch{ public: static void rede( const std::string& text ){ cout << "sprich_menschisch( text );" << endl; } }; class Zwerg{ public: static void rede( const std::string& text ){ cout << "sprich_zwergisch( text );" << endl; } }; class Ork{ public: static void rede( const std::string& text ){ cout << "sprich_orkisch( text );" << endl; } }; // Spielfigur-Template template< typename CharakterT, typename VolkT > class Spieler{ public: void handle(){ VolkT::rede( "Halt, oder ich schiesse!" ); CharakterT::angreifen(); } }; int main() { // Jede Kombination möglich Spieler< Schurke, Zwerg > spieler1; Spieler< Kaempfer, Ork > spieler2; Spieler< Kaempfer, Mensch > spieler3; // Alle gleich zu bedienen aber verschiedenes Verhalten spieler1.handle(); spieler2.handle(); spieler3.handle(); }Du kannst nun jeden Spieler individuell erstellen, und auch jederzeit neue Charaktere oder Völker hinzufügen.
Nennt sich, wie schon gesagt, policy-based design und ist sehr flexibel.
-
Ja, danke, so hatte ich mir das vorgestellt

Sinthoras:
template<class Klasse, class Volk> class NSC : public Klasse, public Volk {... blabla alles hübsch kombinieren...};Entspricht ja auch ungeähr dem, was mich damit meinte.
Eine Frage aber noch:
Wenn ich das hier
template< typename CharakterT, typename VolkT > class Spieler{ public: void handle(){ VolkT::rede( "Halt, oder ich schiesse!" ); CharakterT::angreifen(); } };so (oder so änhlich) mache
template< typename CharakterT, typename VolkT > class Spieler : public CharakterT, public VolkT { public: (...) };Kann ich die Funktionen doch direkt aufrufen oder?
Also in main() z.B.
Spieler<Krieger, Zwerg> Gimli;
Gimli.angreifen();Oder nicht?
Danke jedenfall für Deine Mühe.
-
Hmm brauchst nicht unbedingt erben.
Kannst auch einfach weiterleiten:template< typename CharakterT, typename VolkT > class Spieler{ public: void angreifen(){ CharakterT::angreifen(); } }; //... Gimli.angreifen(); //...Der Vorteil davon ist, dass du keine vtable für die virtuellen Funktionen brauchst. Die Weiterleitung wird garantiert jeder einigermassen schlaue Kompiler wegoptimieren.
-
Aber ganz ohne Vererbug werde ich wohl nicht auskommen, denn ich möchte ja, dass der Magier Zauber bekommt, unser Freund Gimli hier aber nicht.
Wenn ich dann einfach in der Klasse Magier entsprechende Funktionen einbaue, in der Klasse Krieger aber nicht, hat sie am Ende eben auch nur der, der vom Magier erbt.
Also so hatte ich mir das jedenfalls gedacht...
-
Sinthoras schrieb:
Aber ganz ohne Vererbug werde ich wohl nicht auskommen, denn ich möchte ja, dass der Magier Zauber bekommt, unser Freund Gimli hier aber nicht.
Wenn ich dann einfach in der Klasse Magier entsprechende Funktionen einbaue, in der Klasse Krieger aber nicht, hat sie am Ende eben auch nur der, der vom Magier erbt.
Also so hatte ich mir das jedenfalls gedacht...
Ok da ist was dran.