Virtuelle Methoden gar nicht so schlimm?



  • @Volkard

    Kannste auch so, wenn beide Bäume Dictionaries sind, also den op[] wie spezifiziert anbieten.
    

    Nein, das funktioniert IMHO eben nur, wenn der operator [] virtuell ist.



  • class Parser{
    public:
     typedef RedBlackTree Dict;
    private:
     Dict *tr;
    public:
     Dict *GetCommands(void){return this->tr;}
    };
    

    und

    Parser::Dict* d=p.GetCommands();
    


  • Hmmm....
    Irgendwie stehe ich auf dem Schlauch, darüber muss ich einen Moment nachdenken :p



  • Ja nein, jtzt weiss ich natürlich, wieso das nicht geht. Der RedBlackTree hat unter Umständen auch Methoden, welche ein Dictionary nicht hat. Wenn ich nun nicht höllisch aufpasse und aus versehen eine Methode darauf aufrufe, welche das Dictionary resp. die anderen Implementationen von Dictionary (HashMap,BinaryTree) nicht kennen, kann ich den konkreten Typ nicht mehr auswechseln ohne Compilerfehler zu kassieren oder sehe ich das falsch?



  • Ishildur schrieb:

    Ja nein, jtzt weiss ich natürlich, wieso das nicht geht. Der RedBlackTree hat unter Umständen auch Methoden, welche ein Dictionary nicht hat.

    Du wolltest Austauschbarkeit, also ruf sowas nicht auf.

    Ishildur schrieb:

    Wenn ich nun nicht höllisch aufpasse und aus versehen eine Methode darauf aufrufe, welche das Dictionary resp. die anderen Implementationen von Dictionary (HashMap,BinaryTree) nicht kennen, kann ich den konkreten Typ nicht mehr auswechseln ohne Compilerfehler zu kassieren oder sehe ich das falsch?

    Kein Grund, Angst zu haben.

    Kannst während des Parserbauens und immermal zwischendurch auch gerne einen Angstwrapper einsetzen.

    class MinimalDict//Nicht erben
    {
       RedBlackTree imp;
       Value& operator[](key const& k){//Nur anbieten, was alle Dicts können
          return imp[k];
       }
    };
    
    class Parser{
    public:
     typedef MinimalDict Dict;
    private:
     Dict *tr;
    public:
     Dict *GetCommands(void){return this->tr;}
    };
    

    Aber da sehe ich gar keinen Bedarf, weil Du gar nicht wahrnimmst, was Parser::Dict in Wirklichkeit für ein Typ ist. Du programmierst nur gegen die Dictionary-Schnittstelle.



  • Ishildur schrieb:

    "Programmiere gegen Interfaces und nicht gegen Implementierungen",

    Ishildur schrieb:

    Ja nein, jtzt weiss ich natürlich, wieso das nicht geht. Der RedBlackTree hat unter Umständen auch Methoden, welche ein Dictionary nicht hat.

    In dem Fall hast Du den ersten Grundsatz nicht beachtet ...



  • Ishildur schrieb:

    template<typename TKey,typename TValue> class Dictionary{
     virtual TValue &operator[](TKey &Key) = 0x00;
    };
    

    Da steht ja 0x00. Das ist vom Standard her nicht erlaubt. Nur 0 geht.



  • @loks

    In dem Fall hast Du den ersten Grundsatz nicht beachtet ...

    Wieso dass denn? Eine konkrete Implementation kann ja auch mehrere Interfaces implementieren (mache es jtzt der Einfachheit halber ohne Templates):

    class Stack{
     void Push(void*) = 0;
     void *Pop(void)  = 0;
    };
    class Queue{
     void Enqueue(void*) = 0;
     void *DeQueue(void) = 0;
    };
    class Sequence{
     void *operator[](uint32 Index) = 0;
    };
    class Vector:public Sequence,public Stack,public Queue{
     void Push(void*){}
     void *Pop(void){return 0x00}
     void Enqueue(void*){}
     void *DeQueue(void){return 0x00}
     void *operator[](uint32 Index){return 0x00}
    };
    class LinkedList:public Stack{
     void Push(void*){}
     void *Pop(void){return 0x00};
    };
    
    Stack    *s = new Vector();      // OK, programmiere gegen Interface Stack
    Stack    *s2 = new LinkedList(); // OK, programmiere gegen Interface Stack
    Queue    *q = new Vector();      // OK, programmiere gegen Interface Queue
    Sequence *q = new Vector();      // OK, programmiere gegen Interface Sequence
    Vector   *v = new Vector();      // NICHT GUT, programmiere gegen Implementation Vector
    

    In den ersten vier Fällen zwingt mich der Compiler, nur Methoden aufzurufen, welche das jeweilige Interface anbietet.

    @volkard

    Da steht ja 0x00. Das ist vom Standard her nicht erlaubt. Nur 0 geht.

    Hmmm, das hatte ich nicht gewusst. In meinem Visual Studio funktionierts einwandfrei. Ich habe mir angewöhnt, für Adressen die Hex Notation zu verwendenden, damit ich gleich sehe, dass es um eine Adress zuweisung geht und weil es schliesslich einfach im Zusammenhang mit Memorydumps ist.

    class MyClass{
     typedef Stk Vector;
     Stk;
    
     Stk *GetStack(void){return this->v}
    };
    
    MyClass mc;
    MyClass::Stk st = mc.GetStack();
    
    st.DeQueue(); // wird vom Compiler akzeptiert, weil MyClass::Stk eben letzen Endes die Implementation und nicht das Interface Stack repräsentiert.
    
    class MyClass{
     typedef Stk LinkedList; // Nun muss ich doch refactoren, weil st Dequeue auf einem Stack aufgerufen wurde, welche nun von dieser Implementation nicht unterstützt wird.
     Stk;
    
     Stk *GetStack(void){return this->v}
    };
    


  • Ishildur schrieb:

    class MyClass{
     typedef Stk Vector;
     Stk;
    
     Stk *GetStack(void){return this->v}
    };
    
    MyClass mc;
    MyClass::Stk* st = mc.GetStack();
    
    st.DeQueue(); // wird vom Compiler akzeptiert, weil MyClass::Stk eben letzen Endes die Implementation und nicht das Interface Stack repräsentiert.
    

    Dem könnte man abhelfen.

    class MyClass{
     typedef StackWrapper<Vector> Stk;
     Stk;
    
     Stk *GetStack(void){return this->v}
    };
    
    MyClass mc;
    MyClass::Stk* st = mc.GetStack();
    
    st.DeQueue(); // wird vom Compiler NICHT akzeptiert
    

    Es geht also ohne Laufzeitkosten. Aber ist in der Praxis gar nicht nötig, glaube ich.


Anmelden zum Antworten