Laufzeitpolymorphie über Templates?



  • Was willst du mit deinem field denn vor dem Löschen machen?



  • Hambo schrieb:

    mein obiger code solte so ausshen - das delete mtx; am ende ist weg.
    ich möchte einfach auf die variable auch im catch-block zugreifen. schaffe ich das ohne basisklasse nicht?

    Das tut alles nicht das was du erwartest:

    1)an eine Variable mtx im catch-Block kommst du nur wenn diese Variable entweder vordem try-Block, im catch-Block selber oder in der catch-Anweisug defniert hast.
    2) in den beiden if/else-blöcken definierst du jeweils eine neue Variable, die beide auch den Namen mtx haben - diese sind nur im jeweiligen Block der if/else-Anweisung gültig, so dass du, da du ihnen ein mit new erzeugtes Objekt zuweist, ein Speichereck hast. Die Variable mtx, die du vor dem try block definiert hast (und ohne die das alles eh nicht kompiliert), bleibt wie sie ist.

    Die direkte Antwort auf deine Frage ist: du brauchst eine Basisklasse und du müsstest einen Pointer auf diese Basisklasse vor dem Try-Block definieren. UND du musst die Pointer auf die mit new erzeugten Objekte diesem Basispointer zu weisen und nicht irgendwelchen neuen lokalen Variablen mit gleichem Namen.

    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.



  • EDIT 2: Dein Code ist trotzdem nicht kompilierfähig 😉

    bei mir ist er das (linux+ g++ v 4.1.2)

    Was willst du mit deinem field denn vor dem Löschen machen?

    Methoden von field aufrufen und auch field an andere Klassenmethoden übergeben z.B.



  • 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 new fehlschlägt und std::bad_alloc wirft (die nicht von std::runtime_error abgeleitet ist), wird der Speicher automatisch freigegeben. Und das if(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_error wirft? Ja, der entsprechende operator delete wird aufgerufen, um den Speicherplatz freizugeben. Das muss ja der Fall sein, da man den Speicher sonst unweigerlich verlieren würde (denk dran, ein fehlgeschlagenes new kann 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-catch brauchst 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 gegen Mtx_Base& ?

    Übrigens solltest du, wenn du schon unbedingt selbst Speicher verwalten willst, diesen wieder mit delete freigeben.



  • 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 du mtx zuerst 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


Anmelden zum Antworten