[Erledigt] Wie Klassen Template richtig instanzieren?



  • Hallo,

    wie werden Pointer auf Templates deklariert? Zuerst ein Beispiel ohne Pointer:

    #include <iostream>
    using namespace std;
    
    class Saeugetier
    {
    public:
       virtual void Name()
       {
          cout << "Saeugetier\n";
       }
    };
    
    class Katze : public Saeugetier
    {
    public:
       void Name()
       {
          cout << "Katze\n";
       }
    };
    
    class Hund : public Saeugetier
    {
    public:
       void Name()
       {
          cout << "Hund\n";
       }
    };
    
    class Tier
    {
    public:
       template <typename T>
       void Name(T& x)
       {
          x.Name();
       }
    };
    
    void main()
    {
       Katze katze;
       Hund  hund;
    
       Tier tier;
       tier.Name(katze); // Ausgabe: "Katze" -> funktioniert
       tier.Name(hund);  // Ausgabe: "Hund"  -> funktioniert
    
       cin.get();
    }
    

    Obiges Beispiel funktioniert tadellos. Nun soll in der Klasse Tier ein Template Pointer deklariert werden:

    #include <iostream>
    using namespace std;
    
    class Saeugetier
    {
    public:
       virtual void Name()
       {
          cout << "Sauegetier\n";
       }
    };
    
    class Katze : public Saeugetier
    {
    public:
       void Name()
       {
          cout << "Katze\n";
       }
    };
    
    class Hund : public Saeugetier
    {
    public:
       void Name()
       {
          cout << "Hund\n";
       }
    };
    
    template <typename T>
    class Tier
    {
       T Objekt;
    public:
       Tier() : Objekt( T() ) 
       {}
    
       template <typename T>
       void setTier(T x)
       {
          Objekt = x;
       }
    
       void Name()
       {
          Objekt.Name();
       }
    };
    
    void main()
    {
       Katze katze;
       Hund  hund;
    
       Tier<Saeugetier>tier;
    
       tier.setTier(katze);
       tier.Name();          // Ausgabe: "Sauegetier", Wunschausgabe: "Katze"
    
       tier.setTier(hund);
       tier.Name();          // Ausgabe: "Saeugetier", Wunschausgabe: "Hund"
    
       cin.get();
    }
    

    Letztes Beispiel funktioniert nicht. Wie muss programmiert werden, damit bei tier.Name() jeweils "Katze" oder "Hund" ausgegeben wird?



  • Du meinst, ob man einen Template-Typ-Parameter darauf einschränken kann, dass er von einer bestimmten Klasse abgeleitet ist?



  • Sorry, habe meine Frage im Eingangsbeitrag noch einmal neu formuliert (siehe oben).



  • Was du erreichen willst kann man nur raten, da das Beispiel wenig Sinn ergibt.
    Vermutlich das:

    class Tier
    {
       Tier* p;
    };
    

    oder auch das:

    template <class T> class Tier
    {
       T* p;
    };
    

  • Mod

    Exakt so, wie du das willst, geht das nicht. Frag lieber danach, was du warum erreichen möchtest.



  • Nach deinem Edit ist die Sache doch klar - mach aus deinem T ein T* und gut ist.
    Wenn du das Objekt nicht klonst, musst du natürlich darauf achten, dass dein an setTier übergebenes Objekt niemals vor dem Tier stirbt.



  • SeppJ schrieb:

    Exakt so, wie du das willst, geht das nicht. Frag lieber danach, was du warum erreichen möchtest.

    Das gewünschte steht in etwa in der Main Funktion:

    Katze katze;
    Hund  hund;
    
    //Hier "Tier" Instanzieren
    tier.setTier(katze);
    tier.Name();   // Wunschausgabe "Katze"
    
    tier.setTier(hund);
    tier.Name(); // Wunschausgabe "Hund"
    

  • Mod

    Dann braucht es überhaupt keine Templates, sondern einen Zeiger auf Saeugetier.

    edit: Jetzt hast du die Frage wegeditiert, auf die ich geantwortet habe 😞



  • SeppJ schrieb:

    Dann braucht es überhaupt keine Templates, sondern einen Zeiger auf Saeugetier.

    edit: Jetzt hast du die Frage wegeditiert, auf die ich geantwortet habe 😞

    Sorry, habe die ursprüngliche Antwort wiederhergestellt.

    Ja wirklich, mit einem simplen Zeiger auf die Basisklasse "Saeugetier" funktionierts:

    #include <iostream>
    using namespace std;
    
    class Saeugetier
    {
    public:
    	virtual void Name()
    	{
    		cout << "Saeugetier\n";
    	}
    };
    
    class Katze : public Saeugetier
    {
    public:
    	void Name()
    	{
    		cout << "Katze\n";
    	}
    };
    
    class Hund : public Saeugetier
    {
    public:
    	void Name()
    	{
    		cout << "Hund\n";
    	}
    };
    
    class Tier
    {
    	Saeugetier *P;
    public:
    
    	void setTier(Saeugetier *x)
    	{
    		P = x;
    	}
    
    	void Name()
    	{
    		P->Name();
    	}
    };
    
    void main()
    {
    	Katze katze;
    	Hund hund;
    
    	Tier tier;
    
    	tier.setTier(&katze);
    	tier.Name(); // Ausgabe "Katze"
    
    	tier.setTier(&hund);
    	tier.Name(); // Ausgabe "Hund"
    
    	cin.get();
    }
    

    Da hatte ich mich total in die Templates verbissen und die Lösung lag ganz woanders. Danke für die Hilfe!



  • Funktioniert das zuvor geschriebene eigentlich nur mit Zeigern?

    #include "stdafx.h"
    #include <iostream>
    using namespace std;
    
    class Tier
    {
    public:
    	virtual void getName()
    	{
    		cout << "Tier\n";
    	}
    };
    
    class Katze : public Tier
    {
    public:
    	void getName()
    	{
    		cout << "Katze\n";
    	}
    };
    
    void main()
    {
    	Tier  *tier1  = new Katze;
    	tier1->getName(); // => "Katze"
    
    	Tier  tier2;
        Katze katze2;
    	tier2 = katze2;
    	tier2.getName();  // => "Tier"
    
    	cin.get();
    }
    

    Gibt es eine Möglichkeit auch mit Tier als fester Instanz "Katze" auszugeben?



  • Nein.
    Wenn dich (zurecht) das new stört, dann nimm einen std::unique_ptr.



  • Jockelx schrieb:

    Nein.
    Wenn dich (zurecht) das new stört, dann nimm einen std::unique_ptr.

    Danke, werde mir den std::unique_ptr mal näher anschauen.


  • Mod

    Es ginge prinzipiell auch mit Referenzen (was auch nicht viel anders ist als ein Pointer mit schönerer Syntax). Aber auf jeden Fall brauchst du irgendeine Art von Indirektion, denn die Katze muss eine Katze bleiben. Wenn du versuchst eine Katze in ein Tier zu packen, dann bleiben nur die Tier-Eigenschaften der Katze übrig, der Rest wird abgeschnitten*.
    https://en.wikipedia.org/wiki/Object_slicing

    *: Ja, die Beschreibung klingt makaber, der Threadersteller hat eben diese Klassennamen gewählt.



  • SeppJ schrieb:

    Wenn du versuchst eine Katze in ein Tier zu packen, dann bleiben nur die Tier-Eigenschaften der Katze übrig, der Rest wird abgeschnitten.
    https://en.wikipedia.org/wiki/Object_slicing

    Das geschieht aber auch schon mit Zeigern:

    #include <iostream>
    using namespace std;
    
    class Tier
    {
    public:
    	virtual void getName()
    	{
    		cout << "Tier\n";
    	}
    };
    
    class Katze : public Tier
    {
    public:
    	void getName()
    	{
    		cout << "Katze\n";
    	}
    
    	void getFarbe()
    	{
    		cout << "grau getigert";
    	}
    };
    
    void Test(Tier* tier)
    {
    	tier->getName();
    	tier->getFarbe();  // Error C2039: class "Tier" has no member "getFarbe"
    }
    
    void main()
    {
    	Tier *katze = new Katze();
    
    	Test(katze);
    
    	cin.get();
    }
    

    Nachdem ich mein Programm mit den hier gezeigten Vererbungen und Objektübergaben via Basisklassen-Pointer umgeschrieben habe, laufen die darin ablaufenden Grafikrotationen etwas langsamer und nicht mehr flüssig. Sondern in einem festen Intervall mal etwas schneller und langsamer.

    An den Zeigern kann es nicht liegen, weil davon nur 2 erzeugt werden, die über die gesammt Laufzeit des Programms bestehen bleiben.

    Auch die Herausnahme der virtuellen Methoden hat an der "pumpenden" Bewegung nichts geändert. Ist es möglich, dass das "Object-slicing" für die Performance Einbuße verantwortlich sein könnte?



  • Geslict wird da nichts, das muss an einem anderen Teil des Programms liegen. Auch new bringt dein Programm nicht derart zum ruckeln.



  • Die ursprüngliche Version meines Programms wurde im zeitkritischen Grafikteil mit "switch" Anweisungen für zwei Funktionen "Berechne" und "Zeichne()" mit jeweils 10 Switch-Einsprüngen erstellt. Das mit einfacher Vererbung umgeschriebene Programm hat im zeitkritischen Teil eine Basisklasse und entsprechend 10 abgeleitete Klassen.

    Auf dem Programm mit Vererbung laufen die Grafikanimationen / Drehungen im Debug Modus 25% und im Release 33% langsamer. Außerdem laufen die Animationen nicht gleichmäßig schnell, sondern ruckeln ein wenig (laufen kurzzeitig schneller, dann wieder etwas langsamer). Während das Programm via "switch" vollkommen gleichmäßig schnell abläuft.

    Was auf mich den Eindruck hinterläßt, dass Vererbung für zeitkritische Grafikaninmationen anscheinend weniger geigent ist. 😞

    Werde nun versuchen, ob statische Vererbung (CRTP) besser funktioniert.



  • Nach nochmaligem gründlichen Neuaufbau des Programms via einfacher Vererbung, ist die "switch" Version nur noch 5% schneller mit geringfügig flüssiger laufenden Grafikrotationen. Die zuvor berichteten 33% Geschwigkeitsunterschied zu Lasten der Vererbung gehen auf mein Konto, aufgrund schlechter Architektur.


Log in to reply