Klassendesign mit Vererbung und Templates
-
Hallo alle miteinander,
ich möchte gern folgendes Programmieren, weiß aber nicht wie und ob es überhaupt möglich ist: Es geht um die Vererbung von Klassen. Sagen wir ich habe eine Basisklasse und möchte davon viele andere Klassen ableiten. In den abgeleiteten Klassen gibt es einen immer wechselnden Datentyp, der in diesen Klassen fest ist und für den es eine Menge Funktionen gibt. Nun weiß ich nicht ob es möglich ist in der Basisklasse einen Template Platzhalter anzugeben, der in den abgeleiteten Klassen instanziiert wird. Es soll ungefähr so aussehen:
class BASISKLASSE { public: BASISKLASSE(); virtual ~BASISKLASSE(); template <typename T> virtual int setObject(T) = 0; template <typename T> virtual T getObject() = 0; template <typename T> virtual std::list<T> getQueryResult() = 0; private: template <typename T> T tObject; }struct MYOBJECT { int i1; int i2; } class ABGEL_KLASSE : protected BASISKLASSE { public: ABGEL_KLASSE(); virtual ~ABGEL_KLASSE(); virtual int setObject(<MYOBJECT>); virtual <MYOBJECT> getObject(); virtual std::list<MYOBJECT> getQueryResult(); private: <MYOBJECT> tObject; }Nach diesem Pseudocode wird es ja sicher nicht funktionieren, aber wie kann ich das realisieren?
Geht so etwas wie:
template <typename T> class BASISKLASSE { public: BASISKLASSE(); virtual ~BASISKLASSE(); virtual int setObject(T) = 0; virtual T getObject() = 0; virtual std::list<T> getQueryResult() = 0; private: T tObject; }struct MYOBJECT { int i1; int i2; } class ABGEL_KLASSE : protected BASISKLASSE<MYOBJECT> { public: ABGEL_KLASSE(); virtual ~ABGEL_KLASSE(); virtual int setObject(T); virtual T getObject(); virtual std::list<T> getQueryResult(); private: T tObject; }
-
Ich verstehe nicht ganz, was Du eigentlich erreichen willst. Versuch's mal ohne Pseudocode zu beschreiben.
Wozu gibt es denn überhaupt eine Basisklasse und warum verwendest Du "protected inheritance"?
Anmerkungen:
- Es gibt keine virtuellen Elementfunktions-Templates
- Namen, die nur aus Großbuchstaben bestehen, werden üblicherweise nur für Makros und evnetuell auch Konstanzen benutzt.Gruß,
SP
-
Das Großbuchstaben für Makros stehen weiß ich, wollte nur die wichtigen Bestandteile hervorheben.
Jede Klasse steht für eine Tabelle in einer Datenbank und es gibt wiederkehrende set und get- Methoden (z.B. setObject, getObject). Ich möchte eine Basisklasse erzeugen, die den Programmier zwingt in seinen abgeleiteten Klassen bestimmte Methoden zu verwenden (deshalb rein virtuell), die sich jedoch in ihrem Rückgabe- oder Übergabeparameter unterscheiden (in dem Fall tObject).
Der Rück- und Übergabeparametertyp ist mir natürlich noch nicht bekannt. Ich will quasi eine "dynamische" Basisklasse erzeugen, die den Nutzer sowohl eine Schablone (ein template eben) vorgibt, damit er nur noch den spezifischen Typ in seiner abgeleiteten Klasse nachtragen muss und ihn außerdem zwingt einige Methoden zu implementieren, die mit eben diesem Typ arbeiten.Der Unterschied zu einem normalen Template besteht also darin, dass ich die Klasse nicht instanziere und den Typ angebe, sondern in der Ableitung gern einen Typ vergeben möchte.
Wie gesagt: hab keine Ahnung ob das überhaupt möglich ist...
Wenn nicht, wie würdet ihr das implementieren?
-
Was spricht gegen sowas?
template <typename StoredType> class object_store_class { public: void set_object(StoredType const& object ) { m_object = object; } StoredType& get_object() { return m_object; } StoredType const& get_object() const { return m_object; } private: StoredType m_object; }; // Verwendung: class int_store_class: public object_store_class<int> { };Virtuelle Methoden bruachst du da nicht mal.
Gruß
Don06
-
Vielen Dank für den Tipp, dass hat soweit erstmal funktioniert.
Nun habe ich noch eine weitere Frage bezüglich der Konstruktoren der Basis- und der abgeleiteten Klasse:
Der Header meiner Basisklasse sieht folgendermaßen aus (exemplarisch):
#ifndef TTABLES_H #define TTABLES_H #include "dbSQLITE.h" #include <list> template <typename T> class tTableTemplate { public: tTableTemplate(); ~tTableTemplate(); /* write into database */ int dump(); /* load data from database */ int load(QString); /* get object */ T& getObject(); std::list<T> getFirstQueryResult(); private: /* table member */ T tObject; std::list<T> lQueryResult; /* database connection */ SQLITE3 dbHandle; }; #endif // TTABLES_HDie Konstruktoren der Basisklasse und eine exemplarische Funktion aus dem cpp File:
#include "../header/database/tTableTemplate.h" template <typename T> tTableTemplate<T>::tTableTemplate() { } template <typename T> tTableTemplate<T>::~tTableTemplate() { } /* write into database */ template <typename T> int tTableTemplate<T>::dump() { int rv = -1; return rv; }Der Header der abgeleiteten Klasse:
#ifndef TPLAYER_H #define TPLAYER_H #include <QString> #include <QDate> #include <QBool> #include "tTableTemplate.h" struct Player { int iID; QString sNickname; QDate dFirstDate; QDate dLastDate; bool bBuddy; }; class tPlayer : public tTableTemplate<Player> { public: /* consturctor */ tPlayer(); /* destructor */ ~tPlayer(); private: }; #endif // TPLAYER_HUnd die Implementierung der Konstruktoren:
#include "../header/database/tPlayer.h" tPlayer::tPlayer() : tTableTemplate<Player>() { } tPlayer::~tPlayer() { }Wenn ich den Quellcode so übersetze bekomme ich einen Linkerfehler:
"tTableTemplate<Player>::tTableTemplate()", referenced from: tPlayer::tPlayer()in tPlayer.o tPlayer::tPlayer()in tPlayer.o "tTableTemplate<Player>::~tTableTemplate()", referenced from: tPlayer::~tPlayer()in tPlayer.o tPlayer::~tPlayer()in tPlayer.o ld: symbol(s) not foundErst wenn ich noch folgende beiden Zeilen in die Implementierung der abgeleiteten Klasse einfüge, compiliert er korrekt:
template <> tTableTemplate<Player>::tTableTemplate() { } template <> tTableTemplate<Player>::~tTableTemplate() { }Warum muss ich das manuell einfügen, er müsste doch den Konstruktor und den Destruktor der Basisklasse automatisch und unabhängig vom Typ der abgeleiteten Klasse aufrufen?! Ist das überhaupt korrekt, was ich da als Problemlösung in den Quellcode eingefügt habe?
Muss ich das auch für alle weiteren Funktionen machen? Dann wäre ja irgendwie der Aufwand viel höher als der eigentliche Nutzen!Gruß,
Richard
-
Nein, das Problem liegt woanders. Du musst Templates in Headern definieren (nicht nur deklarieren), wenn sie in mehreren Übersetzungseinheiten verwendet werden. Der Grund besteht darin, dass Templates nur Vorlagen für Klassen oder Funktionen sind. Der Compiler muss bei der Instanziierung die Templateparameter einsetzen und erstellt so die eigentliche Klasse/Funktion. Da jede Instanziierung wieder ein wenig anders aussehen kann, reicht es nicht, den Code für alle Templateargumente total einmal zu kompilieren (in einer .cpp-Datei). Es gäbe zwar das Schlüsselwort
export, das wird aber von so gut wie keinem Compiler unterstützt und hält in anderen Implementierungen (Comeau) auch nicht so viel, wie es verspricht.