Template Method Pattern anstatt if-else?



  • Danke vielmals ! Ich habe nun das Template Pattern fast eingebaut - nur ich bleibe an einer stelle irgendwie hängen.

    Mein compiler-fehler lautet:

    main.cpp:147: Fehler: Variable »o_class« kann nicht als vom abstrakten Typ »Class_Base<double>« deklariert werden
    Class_Base.h:46: Anmerkung:   denn die folgenden virtuellen Funktionen sind rein innerhalb »Class_Base<double>«:
    Class_Base.h:81: Anmerkung:      void Class_Base<T>::test() [with T = double]
    

    mein code sieht so aus:

    //in der main:
    Class_Base<double>	o_class;
    if ( a )
    					{
    
    						 o_class = C<double>();
    
    					}
    					else
    					{
                                                 // kommt noch
    					}
    					o_class.my_algorithm();
    

    und die Klassen

    #ifndef GUARD_C_H
    #define GUARD_C_H
    
    #include "Class_Base.h"
    #include <iostream>
    
    template <class T>
    class C : public Class_Base<T>
    {
    	public:
    // 		C<T>() { };
    // 		~C<T>() { };
    
    	private:
    
    	protected:
    		virtual void test() {std::cout << "C" << std::endl;}
    };
    
    #endif
    

    und meine Base-class:

    #ifndef GUARD_Class_BASE_H
    #define GUARD_Class_BASE_H
    
    template <class T>
    class Class_Base
    {
    
    	public:		
    		virtual ~Class_Base() {};
    
    		void		my_Algorithm();
    
    	private:
    
    	protected:
    		virtual void test() = 0;
    };
    
    #include "Class_Base.imp"
    
    #endif
    

    Kann mir jemand sagen warum?
    Sobald ich das = 0 wegnehme kommt die fehlermeldung:

    undefined reference to `Class_Base<double>::test()'
    

    EDIT:
    Anscheinend (für mich neu) darf ich nicht die Abstrakte-Klasse initialisieren sondern nur die Klasse C. Dann gehts.



  • Mati schrieb:

    ...nur ich bleibe an einer stelle irgendwie hängen.

    ... Variable ... kann nicht als vom abstrakten Typ ... deklariert werden

    EDIT:
    Anscheinend (für mich neu) darf ich nicht die Abstrakte-Klasse initialisieren sondern nur die Klasse C. Dann gehts.

    Ja, damit liegst du richtig. Abstrakte Typen sind im wesentlichen nur Schnittstellenzusicherungen. Man kann nur Zeiger des abstrakten Basistyps verwenden um auf beliebige davon abgeleitete Klassen zu verweisen. Eine Initialisierung über den Typ wäre auch wenig sinnvoll, besitzt der doch keine Funktionalität.

    Nur mittels des Zeigers kannst du auch einen Algorithmus zur Programmlaufzeit ändern. Mit einer "normalen" Variable bist du auf den Typ festgelegt.

    cu André



  • Kann denn die Unterscheidungs-methode in der abstrakten Klasse untergebracht werden? Eher weniger oder ? Weil sonst gibts ja nen include-kreis oder so...?

    class1* make_class1( bool input_param )
    {
      if (input_param)
        return new class1(new b_berechner_variante1));
      else
        return new class1(new b_berechner_variante2));
    };
    


  • Doch, natürlich. Je nach dem was dir konkret vorschwebt ist das entweder sinnvoll oder kontraproduktiv.



  • Hmm naja, ich will einfach nur ein switch - statement einbauen um mir halt den Pointer zurückzugeben.

    Also praktich diese Methode:

    class1* make_class1( bool input_param )
    {
      if (input_param) // (input_param == true) ist so sinnvoll wie (((((input_param == true) == true) ... )))
        return new class1_variante1;
      else
        return new class1_variante2;
    };
    

    In die Abstrakte Klasse setzen.

    Nur bekomme ich das so nicht hin.

    Meine header sehen so aus:

    //Class_Base
    //hier will ich die Header Class_Variante1 und Class_Variante2 einfügen aber 
    // es will nicht.
    #include "Class_Variante1"
    #include "Class_Variante2"
    
    class Class_Base
    {
        public:
           Class_Base*   get(int opt) 
            {
                    switch(opt)
                     {
                             case 0:
                                   return new Class_Variante1();
                             case 1:
                                   return new Class_Variante2();
                     }
             }
    }
    
    //Class_Variante1
    #include "Class_Base"
    
    class Class_Variante1 : public Class_Base
    {...};
    
    //Class_Variante2
    #include "Class_Base"
    
    class Class_Variante2 : public Class_Base
    {...};
    


  • Die abgeleiteten Klassen können nicht definiert werden, bevor die Basisklasse bekannt ist. Für die Konstruktion eines Objektes brauchst du aber die vollständige Definition. Folglich kannst du die Erzeugungsmethode mit den new-Aufrufen nicht direkt im Klassenkörper definieren, sondern nur außerhalb (nachdem die abgeleiteten Klassen definiert wurden).
    Praktisch also im cpp-File. Das ist auch von den Abhängigkeiten her sinnvoller.



  • Finix,

    du sagst, das TMP habe nichts mit Templates zu tun und gehst dann dazu über, mit virtuellen Funktionen um Dich zu schmeißen. Da muss ich Einspruch erheben, denn das TMP lässt sich natürlich vorzüglich mit Templates umsetzen – und in C++ würde ich im Zweifelsfall immer diese Methode verwenden, statt virtueller Funktionen.

    Dass diese Methode in einem Thread im C++-Forum nicht einmal erwähnt wird, finde ich schade.



  • Also mich würde brennend interessieren wie das über Templates realisierbar wäre Konrad. Könntest Du vielleicht den vorig geposteten Beispielcode (so schlimm er auch ist) evtl. über Templates ins TMP packen?

    Und was ist der Grund warum über Templates das TMP "besser" ist als über virtuelle Funktionen ?

    @Bashar:

    Praktisch also im cpp-File. Das ist auch von den Abhängigkeiten her sinnvoller.

    wenn ich gleichzeitig mit Templates arbeite - ist das dan auch möglich?
    Weil sowas in ne cpp-zu schreiben geht ja net:

    template <class T>	Class_Base<T>*
    Switch_Algorithm<T>::Get_Algorithm(const int opt_level)
    {
    	switch(opt_level)
    	{
    		case no_opt:
    			return new C1<T>();
    		case opt_1:
    			return new C2<T>();
    	}
    }
    

    Könnte es sein, dass ich hier nicht drumrumkomme die Get_Algorithm in ne eigene Klasse bzw. in eine andere Klasse zu packen?

    Danke



  • das würde ich auch gerne sehen. ging es dem threadstarter nicht darum, zur laufzeit zu entscheiden, was verwendet werden soll. c++ templates werden doch zur compilezeit festgelegt, wie können die virtuelle funktionen ersetzen?



  • Zur obigen Frage mittels der vermeideung von virtuellen Funktionen habe ich folgenden Thread gefunden:
    http://www.c-plusplus.net/forum/viewtopic-var-t-is-192539-and-view-is-next.html

    Nur was da der vorteil sein soll weiß ich (noch) nicht. Evtl. sind die Kosten um virtuelle Funktionen aufzurufen etwas teurer bezüglich Zeit?

    Ich hätte da noch eine Frage und
    ich weiß jetzt nicht ob ich deswegen einen neuen Thread aufmachen soll, deswegen poste ich lieber hier weil es könnte sein dass es eher hier her gehört:

    ich würde gerne folgenden Code so "vereinfachen", dass das switch statement entfällt. Nun bin ich mir nicht sicher ob es überhaupt geht - evtl. irgendwie über virtuelle Funktionen oder so?

    Matrix<double> *A_REAL = NULL;		
    	Matrix<COMPLEX> *A_COMPLEX = NULL;
    
            switch(o_rm.Get_Type(file))
    	{
    		case real:	
    		{
    			std::cout << "\t    Input is REAL!" << std::endl;
    
    			o_rm.Read_Matrix(file, &A_REAL);			
    
    			// Mache was mit A_REAL;		
    
    			delete A_REAL;	
    			break;
    		} 					
     		case complex:
    		{
     			std::cout << "\tInput is COMPLEX" << std::endl;
    
                           o_rm.Read_Matrix(file, &A_COMPLEX);
    
                           // mache was mit A_COMPLEX
    
    		        delete A_COMPLEX;	
    
    			break;		
    		}
    

    In Read_Matrix gibt es Template-Spezifikationen, die unterschiedlich vom Template, die Matrix unterschiedlich aufbauen. Braucht ihr noch mehr Informationen?
    Ich kann also erst zur Laufzeit entscheiden welches Template benutzt wird...
    Das ganze läuft in meiner main(), also in keiner Template Klasse



  • ja schrieb:

    ging es dem threadstarter nicht darum, zur laufzeit zu entscheiden, was verwendet werden soll.

    Das lese ich nirgendwo. Wenn das der Fall ist, sind Templates in der Tat nich geeignet.

    Mati schrieb:

    Und was ist der Grund warum über Templates das TMP "besser" ist als über virtuelle Funktionen ?

    Erstmal, weil es der C++-ige Weg wäre (so arbeitet ja auch die STL, wo möglich). Außerdem ist die Methode wesentlich effizienter als die über virtuelle Funktionen, weil die Auflösung eben zur Compilezeit und nicht zur Laufzeit geschieht.

    Der Code sähe prinzipiell genauso aus, nur dass die Funktionen nicht virtuell gemacht werden und die Ziel-Klasse parametrisiert wird.

    struct Algorithm1 {
        void Method1() {}
        void Method2() {}
    }
    
    struct Alg2 {
        void Method1() {}
        void Method2() {}
    }
    
    void my_method<T>(T const& algorithm) {
        algorithm.Method1();
        // … was anderes …
        algorithm.Method2();
    }
    
    // Aufruf:
    if (flag)
        my_method(Algorithm1());
    else
        my_method(Alg2());
    


  • hmm...wie viel Zeit kostet denn der aufruf virtueller funktionen im vergleich?
    Ist das nur marginal oder doch "zu spüren".
    Bei mir würde es nur einmal passieren.

    Ich schaffe es leider über Templates nicht so ganz:

    template <class T>
    class Algorithm1 
    {
       public:
           void Method1() {}
           void Method2() {}
    };
    
    template <class T>
    class  Alg2 {
        public:
           void Method1() {}
           void Method2() {}
    };
    
    template<class T> T*
    Algorithm::Get_Algorithm(const int opt_level)
    {
         switch(opt_level)
        {
             case 0: return new Algorithm<T>();
             case 1: return new Alg2<T>();
        }
    }
    
    Base<T>::my_method(T* algorithm) 
    {
        algorithm->Method1();
        // … was anderes …
        algorithm->Method2();
    }
    
    // in der main:
    
    Algorithm<double> o_alg;
    Base<double> o_base;
    o_base.my_method(o_alg.Get_Algorithm(0));
    

    mein error:

    Fehler: »C<double>*« kann nicht nach »double*« in return umgewandelt werden
    main.cpp:154:   instantiated from here
    

    Schon klar der feher aber wie kann ich denn den switch in eine eigene Klasse packen um von dort den richtigen klassentyp zu returnen?



  • Konrad Rudolph schrieb:

    Finix,

    du sagst, das TMP habe nichts mit Templates zu tun und gehst dann dazu über, mit virtuellen Funktionen um Dich zu schmeißen. Da muss ich Einspruch erheben, denn das TMP lässt sich natürlich vorzüglich mit Templates umsetzen – und in C++ würde ich im Zweifelsfall immer diese Methode verwenden, statt virtueller Funktionen.

    Dass diese Methode in einem Thread im C++-Forum nicht einmal erwähnt wird, finde ich schade.

    Hier nochmal zum Mitschreiben: das Template Method Pattern hat nichts mit Templates zu tun! Es geht darum dass TMP != Templates und vor allem auch TMP != Template Technik.

    Und in C++ stehst du nun mal oft nicht vor dem "Zweifelsfall" sondern vor Limitierungen der Sprache - sollte ich mich täuschen, wäre ich dir sehr verbunden würdest du mir deine Variante, rein mit Templates, in diesem Fall ("über ein input-flag an und ausschaltbar"), vorstellen.



  • Konrad Rudolph schrieb:

    ja schrieb:

    ging es dem threadstarter nicht darum, zur laufzeit zu entscheiden, was verwendet werden soll.

    Das lese ich nirgendwo. Wenn das der Fall ist, sind Templates in der Tat nich geeignet.

    ich hätte es so verstanden, weil er sich über die vielen if-else beschwert hat.

    @Mati
    Für dein switch Problem könnte das State Pattern was sein.



  • Danke ja - aber um ehrlich zu sein bin ich mir (egal wieviel ich übers state pattern lese) nicht sicher obs gehen wird...ich brauch auch immer einen beispielcode...ich suche schon danach aber sinnvolle einfache C++ bespiele zum state pattern finde ich nicht - wäre nett wenn mir jemand helfen könnte.



  • Mati schrieb:

    ich suche schon danach aber sinnvolle einfache C++ bespiele zum state pattern finde ich nicht - wäre nett wenn mir jemand helfen könnte.

    Du kennst google?
    Du weißt wie man Suchworte in google einsetzt?

    Such mal nach: State Pattern C++

    Und wenn du da wirklich nichts findest (bezweifel ich wenn ich das Überblicke), dann kannst du gerne weiter fragen... (Hinweis: selbst mit state muster c++ und Einschränkung auf deutsche Seiten wird man fündig...)

    cu André



  • Mati schrieb:

    ich suche schon danach aber sinnvolle einfache C++ bespiele zum state pattern finde ich nicht - wäre nett wenn mir jemand helfen könnte.

    Wie wäre es hiermit: http://en.wikipedia.org/wiki/State_pattern



  • Hallo?
    Ich sagte bereits:

    sinnvolle einfache C++ bespiele zum state pattern finde ich nicht

    unter google finde ich nur umfangreiche und nicht gerade erklärende beispiele.

    Aber gut - damit muss ich wohl leben.



  • Mati schrieb:

    @Bashar:

    Praktisch also im cpp-File. Das ist auch von den Abhängigkeiten her sinnvoller.

    wenn ich gleichzeitig mit Templates arbeite - ist das dan auch möglich?

    Nein, dann natürlich nicht (außer mit export etc. blahblah). Das entscheidende ist, dass der Ctor außerhalb der Klasse base definiert sein muss.

    class Base {
    public:
      Base();
    };
    
    // ich schreib hier die Klassendefinition direkt hin, bei dir wäre ein 
    // #include "derived.h" an dieser Stelle
    class Derived : public Base {
    public:
      Derived() { blub(); }
    };
    
    // jetzt ist Derived erst bekannt und kann konstruiert werden
    Base::Base() {
      new Derived() ...
    }
    


  • Ok, dann versuche ich eine konkrete Frage zu stellen:

    Ich bin mir nicht sicher ob ich missverstanden wurde. Ich will in der main() einen Template - Typ zurückbekommen, der anhand eines int-werts entweder double oder COMPLEX ist.

    Ich kann ja aus der main keine Templates initialisieren. Also sowas geht ja in der main nicht:

    Abstract_Matrix ab_mat;
    
    // 0 für real
    // 1 für complex
    Matrix<T>* matrix = ab_mat.Get_Type(0);
    
    //in der Abstract_Matrix
    
    template <class T>
    class Abstract_Matrix
    {
         public:
    
                T* Get_Type(int c)
                {
                         if (c)
                             return new Matrix<double>;
                         else return new Matrix<COMPLEX>;
                }  
    };
    

    so ist es ja alles schwachsinn.

    Nach diesem Satz:

    This is a clean way for an object to partially change its type at runtime.
    

    Unter diesem Link:
    http://en.wikipedia.org/wiki/State_pattern

    gibt es die folgenden Methoden:

    selectPenTool() {
         currentTool.reset(new PenTool);
       }
    
       selectSelectionTool() {
         currentTool.reset(new SelectionTool);
       }
    

    Allerdings ist mir schleierhaft wie ich denn einen Template-Basierten DrawingController bauen sollte, der seinen Template-Typ von double nach COMPLEX ändert bzw. wechseln braucht er ja nicht mal. Er braucht ihn ja nur zu initialisieren....


Anmelden zum Antworten