Anzeige:
Ergebnis 1 bis 11 von 11

Thema: Pointer auf Methode einer Klasseninstanz (C++)

  1. #1
    Registrierter Benutzer
    Registriert seit
    25.11.2002
    Beiträge
    68

    Question Pointer auf Methode einer Klasseninstanz (C++)

    Hi,

    wie die Überschrift bereits ausdrückt, bin ich auf der Suche nach einer Möglichkeit in C++ einen Pointer auf eine Methode einer instanziierten Klasse zu referenzieren. Folgend mal ein Codeschnippsel wie das ganze ablaufen soll (der Code selbst kompiliert leider nicht, da callPointer() myClass::*FunctionPtr und nicht myInheritedClass::*FunctionPtr erwartet);

    Code:
    class myClass {
    public:
        typedef void(myClass::*FunctionPtr)();
        void callPointer(myClass* mc, FunctionPtr fp) {
            mc->*fp();
        }
    };
    class myInheritedClass : public myClass {
    public:
        void myfunc() {}
        void JustToTest() {
            callPointer(this, &myInheritedClass::myfunc);
        }
    };
    Es gibt doch sicherlich noch einen anderen Weg als aus myClass eine template-Klasse zu machen oder eine eigene class Function zu nutzen (reinterpret_cast?)?
    Rein logisch halber müsste es doch möglich sein, auch auf mit dem oben beschriebenen Ansatz eine Lösung zu finden und den Compiler zu "überlisten". Schließlich sind Zieladresse und Instanz bekannt.

    Über jegliche Art von Rückmeldung, ob positiv oder negativ, diesbezüglich wäre ich dankbar und möchte mich schon einmal im voraus bedanken.
    Geändert von dipesh (01-02-2005 um 01:55 Uhr)
    s/(win|dos)/linux/g; #just another fixed pain in the (gl)ass

  2. #2
    Registrierter Benutzer
    Registriert seit
    01.11.2003
    Beiträge
    6
    Probier doch mal die Methode myfunc() virtual in die Basisklasse einzufügen und dann beim Aufruf statt der abgeleiteten Memberfunktion die aus der Basisklasse anzugeben:

    Code:
    class myClass {
    public:
        typedef void(myClass::*FunctionPtr)();
        virtual void myfunc() {cout << "base";}
        void callPointer(myClass* mc, FunctionPtr fp) {
            (mc->*fp)();
        }
    };
    class myInheritedClass : public myClass {
    public:
        void myfunc() {cout << "inherited";}
        void JustToTest() {
            callPointer(this, &myClass::myfunc);
        }
    };
    In der 6. Zeile musste ich die Klammern anders setzen, weil mein Compiler (VC 7) das sonst nicht akzeptiert hat.

  3. #3
    Registrierter Benutzer
    Registriert seit
    05.09.2002
    Ort
    Neuhausen
    Beiträge
    320
    Immer wieder hilfreich.

    Gruss, Andy

  4. #4
    Registrierter Benutzer
    Registriert seit
    25.11.2002
    Beiträge
    68
    Danke für den Tip, Jack's Cancer. Ich hätte vielleicht etwas mehr ausholen sollen um den Grund für diese Fragestellung zu geben. Das Problem ist, dass zahlreiche Klassen von myClass abgeleitet sind. Jede dieser Klassen kann wiederum zahlreiche Funktionen enthalten welche jedoch nur in dieser einen abgeleiteten Klasse Verwendung finden. Sämtliche Funktionen vorher myClass bekannt zu machen würde in einer riesigen Basisklasse enden und würde leider dem Ansatz unterlaufen jede abgeleitete Klasse ihre eigene zusätzliche Funktionalität zuzugestehen. Warum so umständlich? Nun, dazu müsste ich weiter ausholen und ein ziemlich umfangreiches Projekt erläutern. Das würde ich natürlich gerne machen, wenn entsprechendes Interesse besteht (grob gesagt, es geht um ein Scripting-Interface). Natürlich danke ich dir und entschuldige mich für die vielleicht etwas zu eingeschränkte Beschreibung des Sachverhaltes bzw. der Problemstellung. Ist halt nicht leicht ein etwas abstrakteres Problem in wenige übersichtliche Sätze zu pressen :-)

    @RapidMax: Danke vielmals für den mir bisher unbekannten Link. Wird verfolgt, verarbeitet und entsprechend eingesetzt, falls verwertbar.

    @all: Natürlich bin ich für weitere Vorschläge/Links/Ansätze/Ideen jeglicher Form dankbar.
    Geändert von dipesh (02-02-2005 um 01:15 Uhr)
    s/(win|dos)/linux/g; #just another fixed pain in the (gl)ass

  5. #5
    Registrierter Benutzer
    Registriert seit
    01.11.2003
    Beiträge
    6
    Dann fällt mir nur noch eine Lösung mit Templates ein:
    Code:
    class myClass {
    public:
        template<class T> void callPointer(T* mc, void(T::*fp)()) {
            (mc->*fp)();
        }
    };
    class myInheritedClass : public myClass {
    public:
        void myfunc() {}
        void JustToTest() {
            callPointer(this, &myInheritedClass::myfunc);
        }
    };

  6. #6
    Registrierter Benutzer
    Registriert seit
    23.05.2004
    Beiträge
    592
    Ich weiß zwar nicht ob die Seiten ,die RapidMax genannt hat, das Besprechen - gesehen habe ich es nicht - aber das Problem was du hast gehört zum Stichwort "Kontravarianz". Man kann wie du richtig bemerkt hast Zeiger auf Member einer Klasse nicht implizit in Zeiger auf Member einer Basisklasse konvertieren. Der Grund ist, das der Zeiger auf Member der abgeleiteten Klasse möglicherweise auf einen Member zeigt, der in der Basisklasse gar nicht enthalten ist. Deshalb ist (implizit) nur der umgekehrte Weg möglich. Prinzipiell ist es aber erlaubt explizit zu casten. Du müsstest dann halt sicherstellen das das Objekt an den der Zeiger-zu-Member gebunden wird, auch tatsächlich das Element enthält auf den dieser Zeiger-zu-Member zeigt (und auch sonst alle Garantien eingehalten werden natürlich). Soweit nur falls es dir nicht ohnehin schon bewusst war. Allerdings hast du auch angedeutet das du dafür einen reinterpret_cast benutzen würdest. Davon ist abzuraten. Mit diesem Cast entsteht nämlich ein durch C++ nicht spezifiziertes Ergebnis. Du könntest die Funktion also trotzdem nicht (portabel) aufrufen. Du kannst allerdings einen static_cast verwenden. Wenn du willst, kann ich noch ein paar Nummern aus den Standard nachliefern, fallst du das selber nachlesen möchtest (das wäre wohl gar nicht so verkehrt).


    Möglicherweise gibt es aber doch noch einen Weg wie du dein Design so umstellen kannst, das diese "Tricks" unnötig werden. Dann steht zu vermuten das das der bessere Weg ist. Vielleicht ein Callback-Objekt? (RapidMax hatte ja schon in der Richtung was gepostet). Ich glaube es wäre hilfreich, wenn du dir die Frage stellst ob du Pointer to Member aus Effizienzgründen, oder aus einem anderen Grund verwenden möchtest. Im zweiten Fall, denke ich, sollte ein Callback-Objekt ein gangbarer Weg sein.

  7. #7
    Registrierter Benutzer
    Registriert seit
    25.11.2002
    Beiträge
    68
    Ich danke euch für die zahlreichen Anregungen zu der Problemstellung. Eine Callback-Klasse kommt leider aufgrund des Overhead an Quellcode (Klassendeklaration, Ctor, virtual Dtor, virtuelle callHandler, etc.) im Vergleich zu der Uebergabe eines Funktionpointers (lediglich Funktionsdefinition-/deklaration und Uebergabe der Adresse) nicht in Frage. Es geht weniger um Performance als um Menge an Code die in den zahlreichen abgeleiteten Klassen zu definieren sind. Die begründung hört sich vielleicht etwas unverständlich an, macht jedoch Sinn, wenn man bedenkt, dass von der Basisklasse _sehr_viele_ Klassen abgeleitet, geschrieben und verwaltet werden müssen.

    Die Lektüre des angegebenen Links ergab, dass der Weg über Templates wohl der einzig gangbare ist. Ich habe dies nun wie folgt gelöst (der angegebene Quellcode wurde wieder aus den Orginalquellen abstrahiert. Er dient lediglich der Darstellung der Umsetzung und beinhaltet sicherlich einige in der schnelle eingeschobene syntaktische Fehler die jedoch bei der Darstellung des Prinzips ruhig vernachlässig werden können) ;

    Code:
    class myInterface {
    public:
        virtual void callPointer(char* name) {} //dummy um bei Bedarf nach myInterface* casten zu koennen und callPointer aufrufen zu koennen ohne T in myClass zu kennen.
    };
    
    template<class T> class myClass : public myInterface {
    public:
        typedef void(T::*FunctionPtr)();
        void addPointer(char* name, FunctionPtr f) { //Zum Zeitpunkt da die Funktion hinzugefuegt wird, ist T bekannt. Zum Zeitpunkt des Aufrufes jedoch nicht mehr.
            mymap.replace(name, f);
        }
        virtual void callPointer(char* name) {
            FunctionPtr f = mymap[name];
            T *self = static_cast<T*>(this); //Hier haben wir den von "locus vivendi" so richtig erwaehnten static_cast. Danke an der Stelle fuer den Hint :-)
            (self->*f)(); //Der Aufruf. Entspr. Gültigkeitsprüfungen wurden für dieses Beispiel weggelassen.
        }
    private:
        map<char*,FunctionPtr> mymap;
    };
    
    class myInheritedClass : public myClass<myInheritedClass> {
    public:
        void myfunc() {cout << "inherited";}
        void JustToTest() {
            addPointer("myfunc", &myInheritedClass::myfunc); //Zuordnung char* => function
            callPointer("myfunc"); //Der eigentliche Funktionsaufruf kann dann von ueberall aus geschehen. Eine Uebergabe der zugehoerigen myInheritedClass-Instanz ist nicht mehr erforderlich, da sich callPointer ja bereits auf die zugehoerige Instanz bezieht (z.B. myInheritedClassInstanz->callPointer(...))
        }
    };
    Geändert von dipesh (02-02-2005 um 19:00 Uhr)
    s/(win|dos)/linux/g; #just another fixed pain in the (gl)ass

  8. #8
    Registrierter Benutzer
    Registriert seit
    23.05.2004
    Beiträge
    592
    Mit dem static_cast meinte ich das eigentlich noch ein bisschen anders.

    Angenommen derived ist eine von base genau einmal abgeleitete Klasse:
    Code:
    void (base::* pmf)();
    pmf = static_cast< void (base::*)() >(&derived::function);  
                                          
    derived derivedobject;
    base* basepointer = &derived;
    
    (derivedobject.*pmf)(); // --> Dieser Aufruf ist nach meinem besten Wissen legal,
                            //     obwohl der Zeiger-auf-Element einer Basisklasse
                            //     tatsächlich auf eine Element der Abgeleiteten zeigt.
    
    (basepointer->*pmf)(); // --> Und das hier ist ebenfalls legal, weil basepointer auf
                           //     ein derived Objekt zeigt.
    So kannst du dann tatsächlich in *eine* Map Paare von Zeigern auf Objekte und Pointer-to-Member in diese Objekte speichern.
    (Wenn ich nicht den Standard fürchterlich Fehl-Interpretiere)

  9. #9
    Registrierter Benutzer
    Registriert seit
    25.11.2002
    Beiträge
    68

    Wink

    Danke locus vivendi. Aktuell bin ich etwas überbelastet und deshalb bisher noch nicht zum austesten gekommen. Ich danke dir jedoch bereits jetzt für den mehr als interessanten Lösungsansatz.
    s/(win|dos)/linux/g; #just another fixed pain in the (gl)ass

  10. #10
    Registrierter Benutzer
    Registriert seit
    25.11.2002
    Beiträge
    68
    Für jene die über diesen Beitrag stolpern sollten, noch ein interessanter Link zu der Thematik; http://www.codeproject.com/cpp/FastDelegate.asp
    s/(win|dos)/linux/g; #just another fixed pain in the (gl)ass

  11. #11
    Registrierter Benutzer
    Registriert seit
    23.05.2004
    Beiträge
    592
    So ein Zufall, ich war auch gerade dabei, auf kdedevelopers.org zu lesen...

Lesezeichen

Berechtigungen

  • Neue Themen erstellen: Nein
  • Themen beantworten: Nein
  • Anhänge hochladen: Nein
  • Beiträge bearbeiten: Nein
  •