Laufzeitpolymorphie über Templates?
-
Hambo schrieb:
Hmm danke aber das verstehe ich nicht.
Ich kann doch durchaus eine if-else bedingung machen in der ich abfrage was ich haben will. evtl. ist die bezeichnung laufzeit-polymorphie hier verkehrt?
ich meine sowas geht:if (true) Field<double>* mtx = new Field<double>(); else Field<int>* mtx = new Field<int>();Nein, sowas geht nicht. Zumindest nicht über den Scope der If-Else-Abfrage hinaus und damit ist keine Polymorphie möglich.
Hambo schrieb:
try { if (true) Field<double>* mtx = new Field<double>(); } catch (std::runtime_error& ex) { delete mtx; }Wie gesagt, was bringt das? Wenn
newfehlschlägt undstd::bad_allocwirft (die nicht vonstd::runtime_errorabgeleitet ist), wird der Speicher automatisch freigegeben. Und dasif(true)bringt ja wohl gar nichts.Wenn du Laufzeitpolymorphie willst, kommst du nicht an virtuellen Funktionen vorbei. Bzw. die Alternativen sind meistens noch viel mehr Gefrickel (manuelle Fallunterscheidungen).
-
Aber eine wichtige Information hast du nicht geliefert - was du mit alldem erreichen willst. Das sieht irgendwie aus als versuchst du ein Problem evtl. mit den falschen Mitteln zu lösen.
Ich lese eine datei ein in der entweder eine reelle oder komplexe matrix liegt. diese möchte ich templateabhängig einlesen und datenstrukturen darauf generieren. das passiert z.B. in der Field-Klasse - der Name Matrix-klasse wäre hier wohl sinnvoller. Das kann ich aber erst zur laufzeit entscheiden.
anschließend übergebe ich dieses objekt an den algorithmus (anderes objekt z.B.)
-
Wenn new fehlschlägt und std::bad_alloc wirft (die nicht von std::runtime_error abgeleitet ist), wird der Speicher automatisch freigegeben.
passiert das auch im falle std::runtime_error ?
Es ist doch unsauber bei einem catch einfach nur ein return reinzuklatschen und
keinen heap freizugeben oder?
-
Der Algo müsste entweder auch ein Template sein, oder (was ich eher vermute) arbeitet auf einer allgemeinen Matrixklasse, von der das Matrix-Klassentemplate dann das Interface erbt. In dem Fall hast du deine Basisklasse dann schon.
-
Hambo schrieb:
Ich lese eine datei ein in der entweder eine reelle oder komplexe matrix liegt. diese möchte ich templateabhängig einlesen und datenstrukturen darauf generieren.
Schreib dir eine Basisklasse für die Matrix (möglicherweise reines Interface mit pur-virtuellen Funktionen) und leite davon das Template ab.
Hambo schrieb:
passiert das auch im falle std::runtime_error ?
Du meinst, falls der Konstruktor der Klasse einen
std::runtime_errorwirft? Ja, der entsprechendeoperator deletewird aufgerufen, um den Speicherplatz freizugeben. Das muss ja der Fall sein, da man den Speicher sonst unweigerlich verlieren würde (denk dran, ein fehlgeschlagenesnewkann keine gültige Adresse zurückgeben).Hambo schrieb:
Es ist doch unsauber bei einem catch einfach nur ein return reinzuklatschen und
keinen heap freizugeben oder?Wieso freigeben, was schon freigegeben wurde? Das führt zu undefiniertem Verhalten. Das wäre unsauber.
Ein
try-catchbrauchst du, wenn du zusätzlichen manuellen Speicher angefordert hast oder auf eine andere Weise auf Fehler reagieren willst. Aber sicher nicht per se.
-
Wenn du deine Daten an eine andere Funktion lieferst,
hast du sicherlich eine Schnittstellen-Definition.#include <iostream> template<typename T> class Field { public: typedef T type; T value; }; template<typename T> void doSomething(T& t){ typename T::type value = t.value; t.value = value / 3; } int main(int argc, char** argv) { int i=0; if (i==0){ Field<double> mtx; doSomething(mtx); } else { Field<int> mtx; doSomething(mtx); } return 0; }Also hier ist dein Field auf dem Stack -> keine Gefahr wegen Exception
Wenn du jetzt noch nen return type für doSomething einführst, der ja
(hoffentlich) eine Schnittstellendefinition hat, dann kannst alles in
Template-Funktionen behandeln und das Schnittstellen-fähige Object zurückliefern.
-
Gibst ein Grund warum dies nicht vorgeschlagen wird?:
#include <iostream> template<typename T> struct Field { T value; }; template<class T> void doSomething(Field<T>& t) { // t.value = value / 3; // Don't Call it! } template<> void doSomething<int>(Field<int>& t) { t.value = t.value / 3; } template<> void doSomething<double>(Field<double>& t) { t.value = t.value / 3.0; } int main(int argc, char** argv) { Field<double> a; doSomething(a); Field<int> b; doSomething(b); return 0; }
-
Ja danke für die Hilfe.
Ich möchte abe die Objekte auf dem Heap halten. Mir ist jetzt folgendes nicht klar bzw. ich weiß nicht wie ich das folgende Problem lösen könnte. Was mache ich denn wenn ich eine template-basierte klasse habe (die von einer basisklasse erbt) und die eine templatebasierte funktion hat !? Ich muss ja jede Methode in der Basisklasse deklarieren - dort habe ich aber keinen Template-parameter !? Wie mache ich denn das oder geht das nicht?!!! Achtung! Das folgende Bsp. kompiliert nicht!
#include <iostream> class Mtx_Base { public: virtual ~Mtx_Base(); virtual void foo() = 0; }; template<class T> class Mtx : public Mtx_Base { public: void foo() {}; }; class Reader_Base { public: virtual ~Reader_Base(); // !!!!!!!! Geht nicht !? virtual void read(Mtx<T>*& mtx) = 0; }; template<class T> class Reader : public Reader_Base { public: void read(Mtx<T>*& mtx) {} }; int main(int argc, char** argv) { Mtx_Base *mtx = NULL; Reader_Base* reader = NULL; if (true) { Mtx<double>* mtx = new Mtx<double>(); reader = new Reader<double>(); } reader->read(mtx); return 0; }
-
Hambo schrieb:
// !!!!!!!! Geht nicht !? virtual void read(Mtx<T>*& mtx) = 0;Warum eine Referenz auf einen Zeiger auf eine
Mtx<T>-Instanz als Parameter? Spricht etwas gegenMtx_Base&?Übrigens solltest du, wenn du schon unbedingt selbst Speicher verwalten willst, diesen wieder mit
deletefreigeben.
-
Zeus schrieb:
Gibst ein Grund warum dies nicht vorgeschlagen wird?:
#include <iostream> template<typename T> struct Field { T value; }; template<class T> void doSomething(Field<T>& t) { // t.value = value / 3; // Don't Call it! } template<> void doSomething<int>(Field<int>& t) { t.value = t.value / 3; } template<> void doSomething<double>(Field<double>& t) { t.value = t.value / 3.0; } int main(int argc, char** argv) { Field<double> a; doSomething(a); Field<int> b; doSomething(b); return 0; }Hi Zeus,
ich wollte nur zeigen, dass er nicht alles doppelt implementieren muss, wenn
er Templates verwendet.Ich hatte auch erst überlegt, ob ich in meiner Templatefunktion noch eine andere
Funktion aufrufe, die bei double und int unterschiedliche Funktionen aufruft, da
dieser Funktionsname für doube und int existiert.
-
arum eine Referenz auf einen Zeiger auf eine Mtx<T>-Instanz als Parameter? Spricht etwas gegen Mtx_Base&?
ich steh immer noch aufm schlauch.
kann ich denn das Mtx-Objekt dann verändern? Also über *& ? Geht das wenn ich einen pointer setze oder muss ich es wirklcih nur per referenz machen. Aber auch so krieg ichs leider net hin:#include <iostream> class Mtx_Base { public: virtual ~Mtx_Base(); virtual void foo() = 0; }; template<class T> class Mtx : public Mtx_Base { public: void foo() {}; }; class Reader_Base { public: virtual ~Reader_Base(); // !!!!!!!! Geht nicht !? virtual void read(Mtx_Base& mtx) = 0; }; template<class T> class Reader : public Reader_Base { public: void read(Mtx_Base& mtx) {} }; int main(int argc, char** argv) { Mtx_Base *mtx = NULL; Reader_Base* reader = NULL; if (true) { mtx = new Mtx<double>(); reader = new Reader<double>(); } reader->read(mtx); delete mtx; delete reader; return 0; }Meldung ist:
test2.cpp: In function »int main(int, char**)«: test2.cpp:43: Fehler: keine passende Funktion für Aufruf von »Reader_Base::read(Mtx_Base*&)« test2.cpp:22: Anmerkung: Kandidaten sind: virtual void Reader_Base::read(Mtx_Base&)
-
Hambo schrieb:
kann ich denn das Mtx-Objekt dann verändern? Also über *&
Vergiss erst mal Referenzen auf Zeiger, die brauchst du hier nicht.
Beim Aufruf von
read()musst du nicht den Zeiger übergeben, sondern eine Referenz auf das Objekt (und nicht auf den Zeiger!), auf welches der Zeiger zeigt. Also musst dumtxzuerst dereferenzieren.
-
sorry...ich bin zu blöd. auch so gehts nicht (siehe unten).
kannst du mir den code vielleicht zeigen den du meisnt. ich bekomme ständig undefined references oder eben errors.#include <iostream> class Mtx_Base { public: virtual ~Mtx_Base(); virtual void foo() = 0; }; template<class T> class Mtx : public Mtx_Base { public: void foo() {}; }; class Reader_Base { public: virtual ~Reader_Base(); // !!!!!!!! Geht nicht !? virtual void read(Mtx_Bas& mtx) = 0; }; template<class T> class Reader : public Reader_Base { public: void read(Mtx_Base& mtx) {} }; int main(int argc, char** argv) { Mtx_Base *mtx = NULL; Reader_Base* reader = NULL; if (true) { mtx = new Mtx<double>(); reader = new Reader<double>(); } reader->read(*mtx); delete mtx; delete reader; return 0; }
-
Hast du die Fehler überhaupt angeschaut? Achte nur auf die Zeilennummer, du hast einen Tippfehler.
Und natürlich musst du die verwendeten Funktionen auch definieren. Da der rein virtuelle Destruktor von der abgeleiteten Klasse aufgerufen werden muss, sollte er trotzdem eine (leere) Definition erhalten.
-
Danke. Jetzt versteh ichs auch. so klappte es danke!
-
Eh moment noch

kann ich jetzt noch mit dem pointer rangehen? als Matrix_Base*& mtx in die signatur übergeben oder verstehe ich hier noch etwas nicht ?
-
Ah klappt auch
Danke
-
Ich hätte zu unserem Beispiel noch eine frage.
Ich habe jetzt in Mtx einen Member Lines der templatebasiert ist.
So kann ich den natürlich dann in Reader nicht verwenden weil in read()
nur ein Mtx_Base zur Verfügung steht. Ich könnte Lines in Mtx_Base als member verfrachten aber dann kann ich keinen template-typen angeben für Lines dort. Muss ich dann wieder eine Basisklasse Lines_Base erstellen und diese dann in Mtx_Base definieren. Das ganze wird ja immer umständlicher....Kann mir jemand helfen? Danke
#include <iostream> template<class T> class Lines { public: Lines<T> () {} ~Lines<T>() {} }; class Mtx_Base { public: virtual ~Mtx_Base() {} virtual void foo() = 0; }; template<class T> class Mtx : public Mtx_Base { public: Mtx() { lines = NULL;} void foo() {}; Lines<T> * lines; }; class Reader_Base { public: virtual ~Reader_Base() {} // !!!!!!!! Geht nicht !? virtual void read(Mtx_Base*& mtx) = 0; }; template<class T> class Reader : public Reader_Base { public: void read(Mtx_Base*& mtx) { mtx->lines = new Lines<T>(); } }; int main(int argc, char** argv) { Mtx_Base *mtx = NULL; Reader_Base* reader = NULL; if (true) { mtx = new Mtx<double>(); reader = new Reader<double>(); } reader->read(mtx); delete mtx; delete reader; return 0; }
-
Hambo schrieb:
virtual void read(Mtx_Base*& mtx) = 0;Zum dritten Mal: Du brauchst keine Referenz auf einen Zeiger. Probier nicht einfach herum, bis keine Kompilierfehler mehr kommen. Das führt nämlich zu nichts, sondern hindert dich nur daran, die Thematik richtig zu verstehen. Ich habe ja bereits Vorschläge für einen sinnvollen Parametertypen gebracht.
Hambo schrieb:
Ich könnte Lines in Mtx_Base als member verfrachten aber dann kann ich keinen template-typen angeben für Lines dort. Muss ich dann wieder eine Basisklasse Lines_Base erstellen und diese dann in Mtx_Base definieren. Das ganze wird ja immer umständlicher....
Polymorphie bringt dir nichts, solange du in den abgeleiteten Klassen keine Funktionalität implementierst. Momentan hast du nur nichts-tuende Funktionen. Eine weitere Indirektion verschiebt das Problem ebenfalls nur.
Überlege dir, wie du mit
Linesarbeiten kannst. Reicht es nicht, wenn diese Variable erst in den abgeleiteten KlassenMtxbekannt ist?