Memberfunktion in anderer Klasse aufrufen



  • Hallo,

    ich versuche gerade in meiner "Hauptklasse" eine Instanz von 2 anderen Klassen zu erstellen und über den Konstruktor der dritten Klasse einen Funktionspointer auf die Memberfunktion meiner zweiten Klasse zu übergeben. In der dritten Klasse soll dann die Funktion der zweiten Klasse ausgeführt werden und auf Variablen der zweiten Klasse zugreifen. Ich hab schon fast alles versucht der Kompiler zeigt sich jedoch unbeindruckt und gibt mir folgende Fehlermeldung:

    error C3867: 'Klasse2::gebeZahlAus': non-standard syntax; use '&' to create a pointer to member

    Ich habe mal ein kleines Beispiel erstellt:

    main.cpp

    #include "Klasse1.h"
    
    int main()
    {
    	Klasse1 A;
    	return 0;
    }
    

    Klasse1.h

    class Klasse1
    {
    
    public:
    	Klasse1();
    	~Klasse1();
    };
    

    Klasse1.cpp

    #include "Klasse1.h"
    #include "Klasse2.h"
    #include "Klasse3.h"
    
    Klasse1::Klasse1()
    {
    	Klasse2 B;
    	Klasse3 C(B.gebeZahlAus);
    }
    
    Klasse1::~Klasse1()
    {
    }
    

    Klasse2.h

    class Klasse2
    {
    private:
    
    	int iZahl;
    
    public:
    
    	Klasse2();
    
    	~Klasse2();
    
    	void gebeZahlAus();
    };
    

    Klasse2.cpp

    #include <iostream>
    #include "Klasse2.h"
    
    Klasse2::Klasse2()
    {
    	iZahl = 5;
    }
    
    Klasse2::~Klasse2()
    {
    }
    
    void Klasse2::gebeZahlAus()
    {
    	std::cout << iZahl << std::endl;
    }
    

    Klasse3.h

    class Klasse3
    {
    
    private:
    
    	void(*pFunktion)();
    
    public:
    
    	Klasse3();
    
    	~Klasse3();
    
    	Klasse3(void(*funktion)());
    
    	void fuehrFunktionAus();
    };
    

    Klasse3.cpp

    #include "Klasse3.h"
    
    Klasse3::Klasse3()
    {
    }
    
    Klasse3::~Klasse3()
    {
    }
    
    Klasse3::Klasse3(void(*funktion)())
    {
    	pFunktion = funktion;
    }
    
    void Klasse3::fuehrFunktionAus()
    {
    	pFunktion();
    }
    

    Gruß
    Koger



  • Eine Memberfunktion ist eben keine normale Funktion. Es gibt eine entsprechende Syntax für Methodenzeiger, googel mall ein bisschen. Dinge wie std::function sind aber meist angebrachter.



  • Supi danke, daß Du das Problem so fein reduziert hast auf nur sieben Dateien, wo jeweils ganz wenig drin steht. Sehr gut.

    Und ich teste es trotzdem nicht lokal. Zu viel Arbeit.

    Mach eine dicke fette Datei draus, die den selben Fehler macht. Alle Oftproblemlöser haben sowas wie ein fertiges Projekt nebst makefile oder BliBlaBlo bezogen auf /home/opl/src/forum/shortie/main.cpp und können innerhalb von zwei bis acht Sekunden Forencode in das Projekt pasten und meistens vor Ablauf von zehn Sekunden anfangen, zu schreibven, was der Fehler war, gewohnte IDE halt, Fehlermneldungen, die man im Wortlaut kennt,…

    Ich hülfe Dir, wenn Du mir entgegenkämest, daraus eine einzige große in zwei Sekunden Copy-Paste-bare main.cpp zu machen, lehne es ab, es mit sieben zu tun, aber möchte echt betonen, daß die Reduzierung auf nur sieben so supi kleine Dateien viel viel viel besser ist, als SeppJ gewohnt ist.


  • Mod

    Memberfunktionszeiger und Funktionszeiger sind nicht das gleiche! Aus gutem Grund: Zu einem Memberfunktionszeiger gehört auch immer ein Objekt, auf dem die Funktion aufgerufen wird. Was soll hier dieses Objekt sein? Klasse3 hat schließlich keine Objekte der Klasse2. B aus dem Konstruktor von Klasse1? Dann musst du das B und seine Methode zu einem funktionsartigem Objekt verwursteln, so dass man die Methode von B aufrufen kann, als wäre es eine normale Funktion. Das gibt es auch schon fertig in der Standardbibliothek und heißt std::bind (Wobei std::bind sehr mächtig ist und noch viel mehr kann). Der Konstruktor der Klasse3 soll in dem Fall vermutlich auch keinen void(*)() als Parameter haben, sondern entweder einen ganz allgemeinen Templateparameter oder ein std::function.

    Das kann für Anfänger auf den ersten Blick überwältigend sein (Du hast Glück, früher war das noch viel komplizierter 🙂 ). Wichtig ist, bevor du weiter machst, ist, dass dir absolut klar sein muss, wie die Relation von Klassen, Objekten und Methoden untereinander ist. Denn sonst verstehst du das nicht mit den Methodenzeigern und dann verstehst du auch den ganzen Rest nicht.

    PS: Dir ist schon klar, dass man leere Konstruktoren und Destruktoren einfach weglassen kann und sollte?
    PPS: Und volkard hat Recht, dies und anderes ist eine Menge unnötigen Rauschens. Es ist beispielsweise auch ziemlich egal, ob das alles in Klassen steht oder nicht. Ein Minimalbeispiel deines Problems hätte folgendermaßen aussehen können:

    struct Foo // Irgendeine Klasse brauchen wir schon, da es um Memberfunktionszeiger geht
    {
       void bar() {}  // Minimale Memberfunktion
    };
    
    void baz(void (*function)())   // Du willst irgendeine Funktion mit einem Funktionszeiger als Parameter aufrufen
    {
       function();
    }
    
    int main()
    {
      Foo foo;
      baz(foo.bar);   // Und hier dein Fehler, den du illustrieren wolltest
    }
    


  • Schonmal vielen Dank für eure schnelle Hilfe.

    Das Memberfunktionszeiger und Funktionszeiger was anderes sind hatte ich bei meiner Problemsuche herausgefunden, jedoch habe ich keine lauffähige Lösung daraus entwickeln können. Das Minimalbeispiel von SeppJ zeigt das Problem schon auf jedoch dachte ich das noch andere Probleme auftreten können wenn die Geschichte in verschiedenen Klassen geschieht.

    Ich hab das ganze nochmal in eine Datei gepackt um das kopieren ein wenig zu vereinfachen. Der Sinn meines des Aufrufs geht bei meinem Beispiel leider verloren wollte damit nur mein Problem zeigen. Durch das Aufrufen der Funktion pFunktion() im Objekt C soll eine Variable des Objekts B durch die Funktion veraenderZahl verändert werden.

    Die Frage wäre wie ich das Problem über Methodenzeiger lösen könnte? Müsste ich sowohl in Objekt A sowie in Objekt C einen Methodenzeiger erstellen und über den Konstruktor von Objekt C diesen übergeben?

    #include <iostream>
    
    class Klasse3
    {
    
    private:
    
    	void(*pFunktion)();
    
    public:
    
            Klasse3() {}
    
    	Klasse3(void(*funktion)())
    	{
    		pFunktion = funktion;
    	}
    
    	void fuehrFunktionAus()
    	{
    		pFunktion();
    	}
    };
    
    class Klasse2
    {
    private:
    
    	int iZahl;
    
    public:
    
    	Klasse2()
    	{
    		iZahl = 5;
    	}
    
    	void veraenderZahl()
    	{
    		iZahl += 1;
    	}
    };
    
    class Klasse1
    {
    
    public:
    
    	Klasse1()
    	{
    		Klasse2 B;
    		Klasse3 C(B.veraenderZahl);
    	}
    
    };
    
    int main()
    {
    	Klasse1 A;
    	return 0;
    }
    

  • Mod

    Die wahre Frage, die du dir selber immer noch beantworten musst ist, welches Objekt für den Methodenaufruf heran gezogen werden soll. Und sobald du das weißt (bzw. wenn du überhaupt die Problematik verstanden hast), dann sollten dir die bereits gegebenen Antworten weiter helfen. Ansonsten sehe ich derzeit nur eine Wiederholung der Eingangsfrage in verbesserter Formulierung.



  • Das Ziel ist es eigendlich in Objekt C die Variable von Objekt B zu verändern.

    Mein Plan dazu ist ein Zeiger der auf die Memberfunktion in Objekt B zeigt nach Objekt C zu übergeben und von Objekt C somit die Funktion (welche in Objekt B definiert wurde und somit Zugriff auf die Variable hat) aufzurufen.



  • Ich denke, dass
    http://stackoverflow.com/questions/2402579/c-function-pointer-to-member-function und
    http://stackoverflow.com/questions/2298242/callback-functions-in-c
    gute Einstiegspunkte wären.

    Ich denke, es ist am einfachsten, wenn du eine std::function<void()> als Argument nimmst und dann einfach ein Lambda übergibst, das das entsprechende Objekt by-ref gecaputred hat. Aber beachte auch die Lebenszeit der Objekte, d.h. deine Klasse3 darf die Funktion nur so lange aufrufen, wie das zugehörige Objekt noch lebt.

    Vielleicht guckst du auch mal nach std::function http://de.cppreference.com/w/cpp/utility/functional/function

    Soll denn eigentlich immer dieselbe Funktion des übergebenen Objekts aufgerufen werden? -> dann könntest du ja einen Zeiger/eine Ref auf das Objekt übergeben. Ansonsten: mit der Lösung, eine std::function<void()> zu übergeben, bist du absolut flexibel.



  • Danke für die beiden Links die zusammen beschreiben so ziemlich die beiden Teile meines Problem. Jedoch ist mir noch immer nicht klar wie ich einen Zeiger auf eine Memberfunktion als Parameter übergebe.

    Ich muss so wie ich das verstanden habe einen Zeiger auf das Objekt mit übergeben um so die Memberfunktion ausführen zu können.

    Ich hänge hier mal den kläglichen Versuch das irgendwie zum laufen zu bringen dran hab schon alles ausprobiert und finde den Fehler einfach nicht.

    #include <iostream>
    
    class Klasse3
    {
    
    private:
    
    	//Klasse2 *BK;
    	//void (Klasse2::*zeiger)();
    
    public:
    
    	Klasse3() {}
    
    	Klasse3(Klasse2 *zB, void (Klasse2::*zeiger)())
    	{
    		(zB->*zeiger)();
    	}
    
    	void fuehrFunktionAus()
    	{
    
    	}
    };
    
    class Klasse2
    {
    private:
    
    	int iZahl;
    
    public:
    
    	Klasse2()
    	{
    		iZahl = 5;
    	}
    
    	void veraenderZahl()
    	{
    		iZahl += 1;
    		std::cout << iZahl << std::endl;
    	}
    };
    
    class Klasse1
    {
    
    public:
    
    	Klasse1()
    	{
    		Klasse2 B;
    		//void (Klasse2::*zeiger)() = &Klasse2::veraenderZahl;
    		//(B.*zeiger)();		--> funktioniert ohne Probleme nur das übergeben des Zeigers als Parameter des Konstruktors von Objekt C will nicht klappen
    		Klasse3 C(&B, &Klasse2::veraenderZahl);
    	}
    
    };
    
    int main()
    {
    	Klasse1 A;
    
    	return 0;
    }
    

  • Mod

    Da käme dann das schon erwähnte std::bind zum Zuge, das aus einem Memberfunktionszeiger und einem Objekt ein funktionsartiges Objekt (d.h. ein Objekt, welches man wie eine Funktion aufrufen kann) erzeugt, welches man dann in eine std::function packen kann. Oder das von wob erwähnte lamda, aber ich würde hier bind bevorzugen, weil kürzer und einfacher. Effektiv macht beides exakt das gleiche.



  • Was fehlt dir denn noch, was ist dein Fehler?

    Obiges Programm kompiliert durch, wenn man Klasse2 noch am Anfang forward-deklariert und es gibt dann 6 aus. Das wolltest du doch?



  • Hui das war ein ganz doofer Fehler von mir den hätte ich auch sehen können. Wie Wob geschrieben hat lag es an der fehlenden Deklarierung von Klasse2 und ich hab schon an den Methodenzeigern gezweifelt. Hat alles soweit auch bei meinem Hauptprojekt funktioniert.

    Hier nochmal der lauffähige Code falls jemand ein ähnliches Problem haben sollte:

    #include <iostream>
    
    class Klasse2
    {
    private:
    
    	int iZahl;
    
    public:
    
    	Klasse2();
    
    	void veraenderZahl();
    };
    
    class Klasse3
    {
    
    private:
    
    	Klasse2 *BK;
    	void (Klasse2::*zeiger)();
    
    public:
    
    	Klasse3() {}
    
    	Klasse3(Klasse2 *zB, void (Klasse2::*zeiger)())
    	{
    		BK = zB;
    		this->zeiger = zeiger;
    
    	}
    
    	void fuehrFunktionAus()
    	{
    		(BK->*zeiger)();
    	}
    };
    
    	Klasse2::Klasse2()
    	{
    		iZahl = 5;
    	}
    
    	void Klasse2::veraenderZahl()
    	{
    		iZahl += 1;
    		std::cout << iZahl << std::endl;
    	}
    
    class Klasse1
    {
    
    public:
    
    	Klasse1()
    	{
    		Klasse2 B;
    		Klasse3 C(&B, &Klasse2::veraenderZahl);
    		C.fuehrFunktionAus();
    	}
    
    };
    
    int main()
    {
    	Klasse1 A;
    
    	getchar();
    
    	return 0;
    }
    

    Vielen Dank an alle für eure Hilfe

    Gruß
    Koger


Log in to reply