Zeiger auf Methoden



  • Hi all.

    Ich habe eine Klasse A und eine Klasse B.
    Von der Klasse A leite ich verschiedene Klassen ab.
    In Klasse B möchte ich einen Zeiger auf eine Methode in einer der abgeleiteten Klassen speichern und diese Methode möchte ich später auch aufrufen.

    Dazu habe ich in Klasse B vereinbart:

    ...
    typedef void (A::*Funktion)();
    ...
    Funktion MachWas;
    ...
    

    Wenn ich aber nun eine Klasse C habe:

    class C : public A
    {
    public:
    void DieseFunktion(){};
    
    B MyInstance;
    MyInstance.MachWas = &C::DieseFunktion;
    }
    

    Dann sagt mir MinGW, daß er void (C::)() nicht nach void (A::)() konvertieren kann. Ändere ich die Zuweisung in

    MyInstance.MachWas = &A::DieseFunktion;
    

    bekomme ich verständlicherweise die Fehlermeldung, daß DieseFunktion kein Member von A ist.

    Gibt es eine Möglichkeit, mein Vorhaben irgendwie zu realisieren?
    Ziel soll sein, daß B direkt Methoden aus von A abgeleiteten Klassen aufrufen können soll.



  • Was ist in deinem Beispiel der Vorteil gegenüber virtuellen Methoden?
    Ansonsten schau mal boost::function mit boost::bind an, oder boost::signals oder sigc++.



  • Decimad schrieb:

    Was ist in deinem Beispiel der Vorteil gegenüber virtuellen Methoden?
    Ansonsten schau mal boost::function mit boost::bind an, oder boost::signals oder sigc++.

    Ich verstehe die Frage nicht. Wie können mir hier virtuelle Methoden helfen? Die Klasse B soll von verschiedenen Klassen benutzt werden können, die sie nicht kennt. Diese verschiedenen Klassen sind/werden alle von A abgeleitet, Klasse B kennt nur Klasse A. B möchte aber Funktionen der verschiedenen Klassen aufrufen können und will deshalb einen Methodenzeiger speichern.

    Ich würde das gerne ohne boost machen, bzw. machen können.



  • Belli schrieb:

    Ich verstehe die Frage nicht. Wie können mir hier virtuelle Methoden helfen?

    Schau Dich mal nach dem Interface-Pattern um.



  • @Belli: virtual

    struct A
    {
    	virtual void DieseFunktion() = 0;
    };
    
    struct C : A
    {
    	void DieseFunktion() { std::cout << "C"; }
    };
    
    struct D : A
    {
    	void DieseFunktion() { std::cout << "D"; }
    };
    
    struct B
    {
    	typedef void ( A::*Funktion )();
    	Funktion MachWas;
    	A* Instance;
    
    	void Call()
    	{
    		(Instance->*MachWas)();
    	}
    };
    
    int main()
    {
    	C c;
    	D d;
    	B MyInstance;
    	MyInstance.MachWas = &A::DieseFunktion;
    	MyInstance.Instance = &c;
    	MyInstance.Call();
    	MyInstance.Instance = &d;
    	MyInstance.Call();
    
    }
    


  • @David_pb:

    Ja, sehr schön, diese Idee ist mir auch schon durch den Kopf gegangen. Das 'Problem' ist, Klasse A ist schon fertig, und nicht von mir.
    Klasse B ist ebenfalls von Klasse A abgeleitet, und kennt diese deshalb.

    Um diesen Ansatz zu verwirklichen, müsste ich wohl so etwas wie eine 'Zwischenklasse' entwerfen, die zunächst von A erbt, und dann die benötigten virtuellen Methoden deklariert; dann alle anderen Klassen von dieser ableiten.

    Was mich im Moment ein bißchen daran stört ist, daß ich mich dann praktisch von vornherein auf den Methodennamen festlegen muß.

    Vielleicht mal ein bißchen mehr zum Hintergrund:
    A ist die Grundlage für Fensterklassen in Windows.
    In meinem konkreten Fall ist B eine Klasse für einen farbigen Button und C ein Anwendungsfenster - könnte auch mal ein Dialog oder ähnliches sein/werden - die einen Button (B) enthalten soll.
    Wenn nun der Button geklickt wird, soll er eine Methode des Parents aufrufen.
    Da künftig ja sicher verschieden Anwendungen entstehen werden, wird es immer wieder neue Parentklassen geben, die mindestens immer von A abgeleitet sind.
    Idealerweise sollte man da nicht auf einen bestimmten Methodennamen festgelegt sein, sondern einfach 'seinem' Button die Adresse einer beliebigen aufzurufenden Methode mitteilen können.

    Wenn ich das mit den von Tachyon erwähnten Interfaces richtig im Hinterkopf habe, erspart mir das zwar die Einführung einer Zwischenklasse; man erbt AFAIK in dem Fall von zwei Klassen, wobei die zweite eben nur die virtuelle Methode deklariert - aber auch in diesem Fall steht dann der Name der Methode (in der Interface-Klasse) unumstößlich fest.

    Habe ich das wohl so korrekt verstanden?



  • Belli schrieb:

    Ja, sehr schön, diese Idee ist mir auch schon durch den Kopf gegangen. Das 'Problem' ist, Klasse A ist schon fertig, und nicht von mir.
    Klasse B ist ebenfalls von Klasse A abgeleitet, und kennt diese deshalb.

    Dann hoffe ich nur das Klasse A für Ableitung überhaupt geeignet ist (virtueller oder privater Destruktor).

    Eine andere Möglichkeit wäre Beide Klassen in ein Proxy zu verpacken, der Wiederum die Polymorphie umsetzt (Gemeinsame Proxybasisklasse)...

    Wie heißt es so schön: Wenn etwas nicht direkt machbar ist, füge eine Ebene der Indirektion ein.

    cu André



  • [quote="asc"]

    Belli schrieb:

    Dann hoffe ich nur das Klasse A für Ableitung überhaupt geeignet ist (virtueller oder privater Konstruktor).

    Ich weiß - mal wieder - nicht, was ein virtueller Konstruktor ist, aber die Klasse ist als Basisklasse für Windows-Objekte (Controls, Fenster, Dialoge usw.) gedacht.
    Ergänzend zu meinem letzten Posting ist mir noch eingefallen, daß die vorgeschlagenen Lösungsansätze nicht nur die Methodennamen, sondern auch die Anzahl festlegen.
    Wenn ich zB ein Fenster mit n Buttons habe, brauche ich eigentlich Methoden wie:
    Button1Clicked, Button2Clicked ... ButtonNClicked.
    Mir sausen da im Moment so Stichworte wie Adapterklasse, Funktionsobjekt ... durch den Kopf. Ich muß mal sehen, ob ich da was finde, was ich 1. verstehe und 2. gebrauchen kann ...



  • Belli schrieb:

    Ich weiß - mal wieder - nicht, was ein virtueller Konstruktor ist

    Macht nichts, gibt es eh nicht in C++ (vielleicht ja in asC++ 😉 SCNR).



  • jencas schrieb:

    Macht nichts, gibt es eh nicht in C++ (vielleicht ja in asC++ 😉 SCNR).

    Meinte Destruktor, habe ich korrigiert xD...



  • Ein virtueller Destuktor ist notwenig, um den Destruktor einer Instanz, dessen Typ man nicht kennt aufzurufen. (Eine Basisklasse muss aber bekannt sein; das ist auch das Objekt, dass den virtuellen deklariert.

    struct A
    {
       ~A();
    };
    
    struct B : A
    {
       ~B(){}
    };
    
    B* b = new B;
    A* a = b;
    
    delete a; //zerstört b korrekt
    

    Als virtuellen Konstruktor wird manchmal die Technik gennant, wenn man in der Basisklasse eine virtuelle Funktion Clode o.ä anbietet, welche dann überschrieben wird (von der ableitenden Klasse) und dann ist es möglich ein Objekt ohne seinen genauen Typen zu kennen zu erzeugen.

    struct A
    {
     A* Clone ();
    };
    
    struct B : A
    {
     B* Clone (){ return new B; }
    };
    
    B* b = new B;
    A* a = b;
    A* a2 = a->clone(); //erzeugt ein neues B
    

    Zumindest den virtuellen Destruktor sollte dir schon einmal in einem C++ Buch begegnet sein, sofern es Vererbung behandelt.



  • drakon schrieb:

    Zumindest den virtuellen Destruktor sollte dir schon einmal in einem C++ Buch begegnet sein, sofern es Vererbung behandelt.

    Selbstverständlich, hier war aber die Rede von einem virtuellen Konstruktor. Es wäre schön, wenn jetzt noch jemand zum ursprünglichen Problem was sagen könnte.


  • Administrator

    @Belli,
    Entweder du setzt das hier um:

    asc schrieb:

    Wie heißt es so schön: Wenn etwas nicht direkt machbar ist, füge eine Ebene der Indirektion ein.

    Oder du nimmst Boost.Function und Boost.Bind:

    class Foo
    {
    private:
      boost::function<void()> m_function;
    
    public:
      template<typename ClassT>
      void set(void (ClassT::*func)(), ClassT* obj)
      {
        A const* a = obj; // Prüfen ob ClassT von A abgeleitet ist.
                          // Gäbe wahrscheinlich noch besser Methoden,
                          // für eine Überprüfung, bin aber zu faul
                          // mir dazu Gedanken zu machen.
    
        m_function = boost::bind(func, obj); // Setzen.
      }
    };
    

    Falls Boost.Function und Boost.Bind noch eine Stufe zu hoch sind, dann wird es problematisch.

    Grüssli


Anmelden zum Antworten