Zeiger auf Memberfunktionen



  • Hallo,
    ich habe ein Problem bei dem Aufruf von Memberfunktionen, die durch Zeiger in einer multimap referenziert werden, etwa so:

    multimap<int, void (T::*)()>
    

    Hier stellt sich für mich schon die Frage, ob solch ein Konstrukt - da keine Parameter übergeben werden - für eine per this-Call aufgerufene Funktion überhaupt gültig ist? Oder muss hier als zusätzlicher Parameter zumindest noch ein Pointer auf die Klasse angegeben werden?

    Das zweite Problem besteht darin, dass das obige Konstrukt mit den Funktionszeigern nicht auch in der Klasse steckt, aus der die Methoden stammen, sondern selbst wieder in einer Klasse gekapselt sind:

    class A{      // Klasse, deren Member referenziert werden sollen
         public:
         class B{
              multimap<int, void (A::*)()> foo;
         }b;
    
    }
    

    Wobei mehrere Instanzen von A bestehen können und B immer nur jeweils Zeiger auf Member aus der Instanz von A enthält, in der sich B gerade befindet (sry, lässt sich das auch leichter ausdrücken? 😃 ). Kürzer: Es würde mir reichen, den this-Zeiger einer Instanz von A auch in B zu haben, um die entsprechenden Methoden aufrufen zu können.
    Kann ich diesen einfach von A nach B (z.B. bei Konstruktorauruf von A) übergeben bzw. gibt es noch eine einfachere Methode an den this-Zeiger der Instanz von A aus Sicht von B zu kommen? Und lassen sich auch private-Member von A durch die Konstruktion in B aufrufen, oder muss ich irgendeine der Klassen als friend deklarieren?



  • sry aber WHAT??

    das konstrukt ist an sich gültig, bei aufrufen übergibst du das objekt auf
    diese art und weise:

    pobject->*iterator->second(); // object *
    // oder
    object.*iterator->second(); // object
    

    den zweiten teil verstehe ich beim besten willen nicht, was willst du den
    überhaupt erreichen? sieht mir ein bisschen nach double-dispatch aus, vllt
    könnte auch polymorphie dein problem lösen, in dem du statt mehreren methoden
    einer klasse eine virtuelle methode mehrerer klassen einsetzt. dann würde
    die map wegfallen 😉



  • Sorry für das wirre Geschreibe... ich glaub das ist die Cola am späten Abend 😉
    Im zweiten Teil stand einfach nur die Frage, ob es eine schöne und elegantere Möglichkeit gibt, um in B an den this-Zeiger der Klasse A zu kommen, als ihn Klasse B in irgendeiner Weise zu übergeben (z.B. per Konstruktor, Methode).

    Das Problem lässt sich vielleicht nur schwer abstrakt beschreiben, weshalb ich mal ein bisschen konkreter werde:

    Klasse A ist in meinem Programm zumeist irgendein GUI-Objekt (z.B. ein Button, eine Edit-List usw). Klasse B ist ein Event-Handler, der über eine Methode AddEvent verfügt. Über diese Methode füttere ich sozusagen den EventHandler (also die multimap) mit Methoden des GUI-Objektes (bei der WinAPI ein Beispiel WM_LBUTTONDOWN). Somit umgehe ich das Problem, bereits im EventHandler alle möglichen Fälle von Events abfangen zu müssen und kann die Abhandlung der Events in verschiedenen Klassen dynamischer gestalten.
    Im Grund ist dies ein ähnliches Konzept, wie es in C# mit Delegaten praktiziert wird. Ich versuche es gerade nur, auf mein eigenes Programm zu übertragen 🙂

    Sooo... dabei kommt aber das Problem auf, dass ich dem EventHandler einen Zeiger auf die übergeordnete Klasse übergeben muss, damit ich die entsprechenden Methoden aus der GUI-Klasse aufrufen kann. In diesem Fall müsste ich jedoch den EventHandler entsprechend den möglichen übergeordneten Klassen "templatisieren". Ich habe zwar bereits eine Basisklasse für alle GUI-Objekte innerhalb meines System, aber es gibt ein paar Spezialfälle, für die das nicht zutrifft.
    In diesem Fall ist es jedoch so, dass ein EventHandler auch immer nur für ein GUI-Objekt zuständig ist und nicht etwa ein EventHandler für viele Gui-Objekte.
    Daher die Frage, ob es eine einfache Möglichkeit gibt, in verschachtelten Klassen an den this-Zeiger der übergeordneten Klasse zu kommen, anstatt ihn übergeben zu müssen (und den EventHandler in eine Template-Klasse umschreiben zu müssen).
    Ich hoffe, das war verständlicher 😃



  • Wie wärs mit Listener-Interfaces und Polymorphie?

    //Declarations
    struct ClickListener
    {
        virtual void onClicked(args) = 0;
        virtual ~ClickListener(){}
    };
    
    class SomeClickableObject
    {
        public:
        //...
        void addListener(ClickListener & listener);
        //...
        private:
        //...
    };
    
    class ICanHandleClickEvents : public ClickListener
    {
        public:
        //...
        void onClicked(args);
        //...
        private:
        //...
    };
    
    //some Definitions
    void ICanHandleClickEvents::onClicked(args)
    {
        //do something
    }
    
    SomeClickableObject iAmAButtonForExample(...);
    
    ICanHandleClickEvents iHandleClicks;
    
    iAmAButtonForExample.addListener(iHandleClicks);
    


  • Wie wärs mit Listener-Interfaces und Polymorphie?

    Vielen Dank für den Vorschlag. Ich denke dies lässt sich mit nicht allzu viel Aufwand schnell umsetzen. Allerdings wird die Masse der Klassen schnell unüberschaubar, wenn man viele individualisierte Click-Events erstellen möchte. Vielleicht finde ich ja einen geeigneten Mittelweg...
    Hat diesbezüglich solcher Konstrukte / Design Patterns jemand einen (guten) Buchvorschlag? Ich suche kein tiefgreifendes Buch, sondern eher ein thematisch "breiter angelegtes", um sich einen groben Überblick über die Materie zu bekommen. Deutsch oder Englisch ist relativ egal.

    Und dann noch kurz eine Frage zu Templates, sozusagen als Zugabe 😃 :
    Ich habe folgendes Konstrukt

    template <class T>
    class A{
    };
    
    class B{
    private:
    A<B> foo;
    };
    
    class C : public class B{
    };
    

    Muss der Templateparameter Klassentemplates A immer bereits bei der Definition mit angegeben werden? Ich würde gerne, sofern eine von B abgeleitete Klasse instanziert wird, dass der dem Template übergebe Typ nicht "B", sondern z.B. "C" etc. lautet. Geht das oder lassen dies die "strikten Sprachregelungen" nicht zu?



  • Sorry für den Doppel-Push-Post(höhö :p ), aber hat keiner nen Buchvorschlag oder ne mögliche Lösung für das Problem?



  • Evtl wäre boost::function was für dich?



  • Evtl wäre boost::function was für dich?

    Was das eingangs erwähnte Problem betrifft, wäre dies auch eine Möglichkeit, danke.
    Mir ging es aber primär nur um das zuletzt erwähnte Problem, in obigem Konstrukt

    template <class T>
    class A{
    };
    
    class B{
    private:
    A<B> foo;
    };
    
    class C : public class B{
    };
    

    das Klassentemplate A in B nicht mit dem Basisklassentyp initialisieren zu müssen, sondern einen von B abgeleiteten Typen als Templateparameter einzutragen. Wie kann ich das Problem am besten lösen ohne das Klassentemplate in alle Basisklassen zu verlagern?



  • Matzer schrieb:

    das Klassentemplate A in B nicht mit dem Basisklassentyp initialisieren zu müssen, sondern einen von B abgeleiteten Typen als Templateparameter einzutragen. Wie kann ich das Problem am besten lösen ohne das Klassentemplate in alle Basisklassen zu verlagern?

    Kurz gesagt: nicht möglich, zumindest nicht auf die Art und Weise.

    Du könntest natürlich eine Indirektionsstufe hinzufügen (ungetestet und nur ein grobes Beispiel):

    class Base
    {
        public:
            virtual ~Base() {};
    };
    
    template<typename T>
    class TemplateBase : public Base
    {
        protected:
            std::vector<T> foo;
    };
    
    class C : public TemplateBase<C>
    {
    };
    

    Edit: Ggf. wäre auch das "Curiously Recurring Template Pattern" etwas passendes... Kommt halt immer darauf an, was du vorhast.



  • Edit: Ggf. wäre auch das "Curiously Recurring Template Pattern" etwas passendes... Kommt halt immer darauf an, was du vorhast.

    Ok, ich habe jetzt die Basisklasse direkt "templatisiert" und übergebe den Typ der abgeleiteten Klasse direkt bei der Definition derselbigen:

    template <class K>
    class A{
    
    };
    
    template <class T>
    class Base{
    A<T>;
    }
    
    class Derived : public Base<Derived>{
    
    };
    

    Allerdings habe ich ein Problem beim Konstruktor. Der sieht für die Basisklasse wie folgt aus:

    Base(std::string foo, int bar){}
    

    und in der Basisklasse rufe ich diesen wie folgt auf:

    Derived(std::string foo2,int bar2) : Base(foo2,bar2){}
    

    Ich hatte es zwischenzeitlich auchmal mit

    Derived(std::string foo2,int bar2) : Base<Derived>(foo2,bar2){}
    

    ausprobiert, allerdings erhalte ich in beiden Fällen die Meldung:

    'Base<T>': Kein geeigneter Standardkonstruktor verfügbar with T = Derived
    ... 
    Bei der Kompilierung der  Klassen-template der Base<T>::Base(std::string,int)-Memberfunktion
    

    Wobei er hier immer auf die Definition des Basisklassekonstruktors zeigt.
    Ich schaue jetzt schon 10 Minuten auf dessen Definition, allerdings fällt mir kein Fehler auf. Ist der Aufruf des Basisklassenkonstruktors so überhaupt richtig?



  • Matzer schrieb:

    Ok, ich habe jetzt die Basisklasse direkt "templatisiert"

    Wenn du die Basisklasse selbst als Template definierts, musst du nur bedenken das sie nicht mehr als Basisklasse für Polymorphie dient (Sprich: Über den Zeiger kannst du nicht mehr alle abgeleiteten Klassen ansteuern). Je nach Konzept ist das okay, solltest nur in den Hinterkopf behalten (Wenn ich mit Vererbung arbeite, will ich häufig dynamische Polymorphie einsetzen).

    Matzer schrieb:

    ...

    Base(std::string foo, int bar){}
    

    Objekte solltest du nicht "per Wert" sondern als konstante Referenzen übergeben. Also lieber: Base(std::string const & foo, int bar)

    #include <string>
    
    template<typename T>
    class Base
    {
      protected:
        Base(
          std::string const & foo,
          int bar);
    };
    
    template<typename T>
    Base<T>::Base(
      std::string const & foo,
      int bar)
    {
    }
    
    class Inherited : public Base<Inherited>
    {
      public:
        Inherited(
          std::string const & foo,
          int bar);
    };
    
    Inherited::Inherited(
      std::string const & foo,
      int bar)
    :  Base<Inherited>(foo, bar)
    {
    }
    
    int main()
    {
      Inherited i("test", 1);
    }
    

Anmelden zum Antworten