Methodenzeiger innerhalb einer static Methode



  • Ich bin gerade dabei, mich mit Klassen zu beschäftigen und wollte eine Klasse machen, die auf bestimmte Kommandos reagiert, aber wo man die Kommandos auch abändern kann.

    #include <iostream>
    
    class TEST {
    public:
        int (*commandoA) ();
        int (*commandoB) ();
    
        static int doCommando (int);
    };
    
    int TEST::doCommando (int commandoCode) {
        if (commandoCode == 1)
            return TEST::commandoA ();
        else
            return TEST::commandoB ();
    }
    
    int TEST::commandoA () {
        std::cout << "Kommando A" << std::endl;
        return 0;
    }
    
    int TEST::commandoB () {
        std::cout << "Kommando A" << std::endl;
        return 0;
    }
    
    int newCommandoA () {
        std::cout << "neues Kommando A" << std::endl;
    }
    
    int main () {
        TEST test;
        test.doCommando (1);
        test.commandoA = newCommandoA;
        test.doCommando (1);
    
        return 0;
    }
    

    Ziel soll es sein, dass die Klasse ein Standardverhalten von commandoA und commandoB anbietet, welches man aber durch Übergeben einer eigenen Funktion ändern kann. Jetzt sagt er mir aber immer, dass ein nicht-statischer Memberverweis relativ zu einem bestimmten Objekt sein muss. Was heißt das und wie muss ich da ran gehen?


  • Mod

    Mehrere Probleme in deinem Code. Genauer gesagt: Inkonsistenzen. Wie diese zu lösen sind, musst du selber wissen, denn nur du weißt genau, wie das Verhalten am Ende aussehen soll.

    1. Sollen commandoA und commandoB nun Funktionszeiger sein oder Memberfunktionen? Du deklarierst sie als Funktionszeiger, du definierst sie als Methoden.
    2. Sollen die Funktionszeiger nun allgemeine Funktionszeiger sein oder Memberfunktionszeiger? Du deklarierst normale Funktionszeiger, aber aufgrund der Inkonsistenz Nummer 1 bin ich mir nicht so sicher, ob du das wirklich möchtest.
    3. static oder nicht? Deine Funktionszeiger sind nicht-statische Member der Klasse, aber doCommando ist eine statische Methode. Da drin kann man logischerweise keine nicht-statischen Member benutzen.



  • SeppJ schrieb:

    Mehrere Probleme in deinem Code. Genauer gesagt: Inkonsistenzen. Wie diese zu lösen sind, musst du selber wissen, denn nur du weißt genau, wie das Verhalten am Ende aussehen soll.

    1. Sollen commandoA und commandoB nun Funktionszeiger sein oder Memberfunktionen? Du deklarierst sie als Funktionszeiger, du definierst sie als Methoden.
    2. Sollen die Funktionszeiger nun allgemeine Funktionszeiger sein oder Memberfunktionszeiger? Du deklarierst normale Funktionszeiger, aber aufgrund der Inkonsistenz Nummer 1 bin ich mir nicht so sicher, ob du das wirklich möchtest.
    3. static oder nicht? Deine Funktionszeiger sind nicht-statische Member der Klasse, aber doCommando ist eine statische Methode. Da drin kann man logischerweise keine nicht-statischen Member benutzen.

    Ok danke für die Denkanstöße.

    1. Es sollen Memberfunktionen sein, aber eben auch Funktionszeiger. Ich dachte man kann das in einem Schritt lösen ...
    2. Normal sollen sie auf eine Methode zeigen, die das Standardverhalten darstellt. Allerdings soll dieses Standardverhalten auch geändert werden können. Da ich ja nicht für ein Klassen-Objekt eine bestimmte Ableitung definieren kann, sondern wenn dann nur für die ganze Klasse wollte ich das so machen.
    3. doCommand sollte bestenfalls static sein, damit es eine feste Speicheradresse bekommt.

    Was mir dank dir jetzt aufgefallen ist, ich kann ja nicht einfach in einen Zeiger meine Funktion packen. Also ich meine, ich kann keinen Zeiger erstellen und dann mit diesem (nicht initalisierten) Zeiger eine Funktion deklarieren.
    Das muss sich ja dann für den Compiler so anhören: "Ich will einen Zeiger auf eine Funktion. Jetzt packe in den Zeiger diese Funktion rein.". Und dann müsste der Compiler antworten: "Hier bitte, ein Zeiger. Aber was bitte für eine Funktion? Ich hätte gern eine Adresse!". Oder?



  • Eine Idee, die ich schon vorher hatte: ich lege die Funktionszeiger an, erstelle zwei Methoden und weise diese Methoden dann dem Funktionszeiger zu. Das fände ich aber nicht so schön, ich habe gehofft, das "eleganter" lösen zu können. Gibt es da dennoch einen Weg, was ungefähr meinem derzeitigen Versuch entspricht?


  • Mod

    klasse schrieb:

    3. doCommand sollte bestenfalls static sein, damit es eine feste Speicheradresse bekommt.

    😕 Du hast merkwürdige Vorstellungen, wie ein Computerprogramm funktioniert.

    Was soll denn deiner Meinung nach der Grund sein, wieso dies erstrebenswert wäre?

    Und nur so zur Info: Alle Funktionen haben sowieso immer* feste Speicheradressen, egal ob Member, frei, const, static, virtual oder sonst was.

    Ich muss anhand deiner "Erläuterungen" leider feststellen, dass du ein paar komische Vorstellungen darüber hast, wie C++ funktioniert. Daher ist nicht so wirklich klar, was für dich eine geeignete Lösung wäre, denn du stellst wahrscheinlich die falschen Fragen. Aber trotzdem kann ich dir ein paar Antworten geben, die zu ähnlichen Fragen passen:
    Zur Laufzeit auswählen, wie ein Objekt sich verhält:
    https://en.wikipedia.org/wiki/Strategy_pattern
    Zur Compilezeit auswählen, wie ein Objekt (oder eine ganze Klasse von Objekten) verhält:
    https://en.wikipedia.org/wiki/Policy-based_design

    Kann auch gut sein, dass das alles viel zu kompliziert ist und du ein ganz einfach zu lösendes Problem hast. Am besten wäre es wohl, wenn du mal ein ausführliches Beispiel bringst, was du dir warum wünscht.

    *: Jetzt erwähnt gleich irgendein Klugscheißer, dass du den Code einer Funktion auch in den Freispeicher kopieren kannst und dort ausführen kannst...



  • Hmm. Das ist ja echt haarig. Also falls das was nützt: die doCommand muss später noch mit stdcall aufgerufen werden, weil der Stack von doCommand auch aufgeräumt werden soll ....



  • klasse schrieb:

    die doCommand muss später noch mit stdcall aufgerufen werden, weil der Stack von doCommand auch aufgeräumt werden soll ....

    Erklär das mal genauer.



  • So wie ich das verstanden habe, willst du das hier ohne den Schritt über die doFunc-Funktion gehen zu müssen machen:

    #include <iostream>
    struct A
    {
    	int doFunc()
    	{
    		if (func) return func(*this);
    		return -1;
    	}
    	int (*func)(A& a);
    };
    int func(A& a)
    {
    	// do something
    }
    int main()
    {
        A a;
        a.func = func;
        a.doFunc();
    }
    

    Was allerdings nicht geht, da Funktionen in C++ nicht wirklich Member im Sinne von Membervariablen (welche tatsächlich von Klasse zu Klasse seperat existieren) sind.
    Funktionen in C++ kann man sich genauso wie globale Funktionen vorstellen wobei jede Funktion nur einmal im Speicher existiert aber zusätzlich der this-Pointer impliziet mitgegeben wird.
    Was in C (ohne OOP) also so aussieht:

    typedef struct
    {
        int a;
    } A;
    int func(A* a)
    {
        a->a = 5;
    }
    int main()
    {
        A a;
        func(&a);
    }
    

    Sieht in C++ so aus:

    struct A
    {
        int func()
        {
            this->a = 5;
        }
        int a;
    }
    int main()
    {
        A a;
        a.func();
    }
    

    Wobei dein Programm selbst während der Laufzeit schon längst nicht mehr weiß, wo sich die Funktion func befindet.
    Bei virtuellen Funktionen hingegen sieht das dann wieder anderst aus.

    Du wirst es also so machen müssen, wie ich dir das oben aufgeführt habe.



  • Neben den Unklarheiten, die hier schon erwähnt wurden wollte ich einfach mal darauf hinweisen, dass Du festlegen solltest, ob Du Bezeichner deutsch oder englisch wählst. Englisch ist eher üblich. "commando" ist so eine Mischung. Deutsch wäre "Kommando" oder englisch "command".


  • Mod

    tntnet schrieb:

    "commando" ist so eine Mischung. Deutsch wäre "Kommando" oder englisch "command".

    Och, englisches "commando" gibt's durchaus auch. Die Frage ist, ob der Threadersteller den Soldaten oder den Bekleidungsstil meint 😉



  • Danke für die viele Hilfe. Der entscheidende Denkfehler war, dass Methoden nicht wie Membervariablen behandelt werden, also nicht für jedes Objekt "neu" angelegt werden.

    Mr.Long schrieb:

    Sieht in C++ so aus:

    struct A
    {
        int func()
        {
            this->a = 5;
        }
        int a;
    }
    int main()
    {
        A a;
        a.func();
    }
    

    doFunc wollte ich schon nutzen, als eine Art Event-Handler. Bei dem Beispiel ist das Problem, dass ich innerhalb einer statischen Funktion nicht mit this arbeiten kann. Ich habe auch keine Ahnung wie ich das doch hinbekomme, weswegen ich der doFunc einfach ein Objekt der Klasse mitgebe und die Funktion A oder B über das Objekt aufrufe. Das funktioniert jetzt auch so:

    Variante 1 (wenn doCommand static sein muss)

    #include <iostream>
    
    class TEST {
    public:
    	TEST ();
    
    	int (*commandA) ();
    
    	static int doCommand (TEST *test, int);
    
    private:
    	static int innerCommandA ();
    };
    
    TEST::TEST () {
    	TEST::commandA = TEST::innerCommandA;
    }
    
    int TEST::doCommand (TEST *test, int commandoCode) {
    	if (commandoCode == 1)
    		return test->commandA ();
    }
    
    int TEST::innerCommandA () {
    	std::cout << "Kommando A" << std::endl;
    	return 0;
    }
    
    int newCommandA () {
    	std::cout << "neues Kommando A" << std::endl;
    	return 0;
    }
    
    int main () {
    	TEST test;
    	test.doCommand (&test, 1);
    	test.commandA = newCommandA;
    	test.doCommand (&test, 1);
    
    	return 0;
    }
    

    Variante 2 (wenn doCommand nicht static sein muss)

    #include <iostream>
    
    class TEST {
    public:
    	TEST ();
    
    	int (*commandA) ();
    
    	int doCommand (int);
    
    private:
    	static int innerCommandA ();
    };
    
    TEST::TEST () {
    	TEST::commandA = TEST::innerCommandA;
    }
    
    int TEST::doCommand (int commandoCode) {
    	if (commandoCode == 1)
    		return this->commandA ();
    }
    
    int TEST::innerCommandA () {
    	std::cout << "Kommando A" << std::endl;
    	return 0;
    }
    
    int newCommandA () {
    	std::cout << "neues Kommando A" << std::endl;
    	return 0;
    }
    
    int main () {
    	TEST test;
    	test.doCommand (1);
    	test.commandA = newCommandA;
    	test.doCommand (1);
    
    	return 0;
    }
    

    Ich habe das Wort static anscheinend nicht richtig verstanden. Zumindest weiß ich nicht, warum ich Variante 2 nicht auch anwenden kann, wenn ich doCommand als static ausweise. Was passiert denn im Speicher mit Methoden, die als static deklariert werden?



  • Statische Methoden (Klassenfunktionen) sind technisch nichts anderes wie freie Funktionen (d.h. sie sind unabhängig von einer Klasseninstanz).

    Im Speicher gibt es keinen Unterschied zwischen den verschiedenen Funktionen.
    Einzig bei (nichtstatischen) Klassenfunktionen wird intern noch die Klasseninstanz (this) als Parameter übergeben.



  • Th69 schrieb:

    Statische Methoden (Klassenfunktionen) sind technisch nichts anderes wie freie Funktionen (d.h. sie sind unabhängig von einer Klasseninstanz).

    Im Speicher gibt es keinen Unterschied zwischen den verschiedenen Funktionen.
    Einzig bei (nichtstatischen) Klassenfunktionen wird intern noch die Klsseninstanz (this) als Parameter übergeben.

    Achso ... gibt es da einen bestimmten Grund für, dass der statischen Methode die Instanz nicht übergeben wird? Prinzipiell ist das Wort static ja wohl nützlich innerhalb von Klassen um bspw. einen Zähler einzurichten, aber technisch sollte es doch möglich sein, einer "globalen" Methode (also mit static definiert) die Instanz zu überreichen, oder? Ich sehe irgendwie keinen Grund, dass das nicht möglich ist (bzw. es ist ja nicht möglich, ich sehe aber keinen Grund, warum das so implementiert wurde/wird)



  • klasse schrieb:

    Th69 schrieb:

    Statische Methoden (Klassenfunktionen) sind technisch nichts anderes wie freie Funktionen (d.h. sie sind unabhängig von einer Klasseninstanz).

    Im Speicher gibt es keinen Unterschied zwischen den verschiedenen Funktionen.
    Einzig bei (nichtstatischen) Klassenfunktionen wird intern noch die Klsseninstanz (this) als Parameter übergeben.

    Achso ... gibt es da einen bestimmten Grund für, dass der statischen Methode die Instanz nicht übergeben wird? Prinzipiell ist das Wort static ja wohl nützlich innerhalb von Klassen um bspw. einen Zähler einzurichten, aber technisch sollte es doch möglich sein, einer "globalen" Methode (also mit static definiert) die Instanz zu überreichen, oder? Ich sehe irgendwie keinen Grund, dass das nicht möglich ist (bzw. es ist ja nicht möglich, ich sehe aber keinen Grund, warum das so implementiert wurde/wird)

    static (in Klassen) heißt in C++ einzig und allein, dass du keinen this-Pointer mit übergibst, also praktisch nicht auf this zugreifen kannst.
    Eine statische Funktion hat, wenn man so will nichts mit der Klassen an sich zu tun.
    In den meisten Anwendungsfällen wird static bei Funktionen auch nur verwendet um sowas wie Kapselung zu erzeugen und vielleicht noch um statische Funktionen als private oder protected zu deklarieren.
    Aber trotzdem, bei vielen Anwendungsfällen wo static verwendet wird, kann man es auch ohne static lösen (also als globale Funktion).

    Mir fällt aus dem Stehgreif auch nur ein Fall ein wo man ohne static nicht auskommt.



  • Mr.Long schrieb:

    kann man es auch ohne static lösen (also als globale Funktion).

    Das war auch meine erste Lösung für doCommand (ist halt nur ein Minimalbeispiel und doCommand macht in diesem Beispiel nicht das, was es eigentlich machen wird, bloß vom zu wählenden Lösungsansatz her sind das Beispiel und die später richtige Implementierung gleich).

    Mr.Long schrieb:

    Mir fällt aus dem Stehgreif auch nur ein Fall ein wo man ohne static nicht auskommt.

    Darf ich fragen, welches das wäre?



  • Wenn du Funktionstemplateparameter nur teilweise spezialisieren willst.


Log in to reply