Funktions Pointer + Vererbung = Problem


  • Mod

    drakon schrieb:

    camper schrieb:

    Ein Memberfunktionszeiger der Klasse A erlaubt es, eine solche Memberfunktion mit jedem beliebigen A-Objekt - auch solchen die keine Basisklassensubobjekte eines B-Objektes sind - aufzurufen. Das ist natürlich gefährlich, falls die der Zeiger tatsächlich auf eine Memberfunktion, die nur die Klasse B hat, zeigt. In diesem Falle muss dafür gesorgt werden, dass das Objekt, auf das der Memberfunktionszeiger angewendet wird, tatsächlich ein Basisklassensubobjekt der eines B-Objektes ist, sonst resultiert undefiniertes Verhalten. Konsequenterweise kann ein Memberfunktionszeiger der Klasse B nicht implizit in einen Memberfunktionszeiger der Klasse A konvertiert werden (umgekehrt dagegen schon: Stichwort Kontravarianz). Ein static_cast ist hier nötig.

    addFunction(static_cast<method_ptr>(&B::y));
    

    Hmm. Das habe ich zuerst auch gedacht, aber wollte dann mal noch ausprobieren. Und siehe da:

    error C2440: 'static_cast' : cannot convert from 'void (__thiscall B::* )(void)' to 'A::method_ptr'

    Korrekt, habe nicht richtig hingeschaut. A ist ein virtuelle Basis B. In diesem Falle ist diese Konvertierung tatsächlich unmöglich (da der umgekehrte Weg, die Konvertierung von A nach B, die implizit beim Aufruf über den Memberfunktionspointer gemacht werden muss, bei einer virtuellen Basis nur bei Vorliegen dynamischer Typinformationen möglich ist).



  • Ok, wie kann ich mein Problem dann anders lösen?

    Ich beschreib nochmal so gut wie möglich was das Programm machen soll: Es gibt eine Klasse A. In dieser Klasse wird ein String (der in der cmd Zeile eingegeben wird) ausrechnet. Also zum Beispiel "3+5*4". Soweit so gut. Weiters sollten nun eine sin und eine cos Funktion eingebaut werden. Das habe ich gemacht indem ich sin bzw cos Funktionen in A angelegt hab und dann mit einem Funktionspointer aufgerufen habe. Warum überhaupt mit einem Pointer? Im 3. Schritt soll eine Klasse B gemacht werden in der dann nochmal 7 Rechenfunktionen (min(x), max(x), usw.) dazukommen sollen. B soll abgeleitet von A werden.

    Ich hab also eine Klasse A in der es eine Map gibt. In dieser Map sind Name und Pointer der Funktionen gespeichert. Jetzt soll ich eine Klasse B machen und neue Funktionen dazu geben können. Dazu brauch ich damit natürlich wieder den Namen und den Pointer.

    In Summe sollte es am Ende so aussehen:

    Klasse A: diverse Rechenoperationen, sin & cos Funktion, "anmelde" Funktion
    Klasse B: 7 weitere Funktionen.

    Diese Funktionen müssen sich mit Name und Pointer "anmelden" (das es sie gibt). Die "angemeldeten" Funktionen sind in einer Map gespeichert.

    LG
    Daniel



  • abzy schrieb:

    Ok, wie kann ich mein Problem dann anders lösen?...

    Das hier:

    abzy schrieb:

    ...Das habe ich gemacht indem ich sin bzw cos Funktionen in A angelegt hab und dann mit einem Funktionspointer aufgerufen habe. ...

    Klasse A: diverse Rechenoperationen, sin & cos Funktion, "anmelde" Funktion
    Klasse B: 7 weitere Funktionen.
    ...

    lassen. 😉

    Es sieht ein wenig so aus, als wärst Du dem klassischen "OO ist, wenn man alles in Klassen packt"-Irrtum aufgesessen (oft in Kombination mit "Gute Programmierung ist, wenn man OO (nach obigem Verständnis) macht").

    sin, cos und all die anderen Rechenoperationen, sind keine guten Kandidaten für Memberfunktionen, sondern ideal für "freie Funktionen".
    Was sollen Deine Klassen eigentlich sein ? Was ist ein A ... und was unterscheidet es von einem anderen A ?
    Wenn man einfach Funktionen bündeln will, sind namespaces prima.
    Dann kannst Du auch die von Dir gewohnten Funktionspointer ("Nicht-member-Pointer") verwenden.
    C++ unterstützt Dich durch overloading und Operatoren ziemlich in dieser Vorgehensweise.

    Gruß,

    Simon2.



  • sin, cos und all die anderen Rechenoperationen, sind keine guten Kandidaten für Memberfunktionen, sondern ideal für "freie Funktionen".
    Was sollen Deine Klassen eigentlich sein ? Was ist ein A ... und was unterscheidet es von einem anderen A ?
    Wenn man einfach Funktionen bündeln will, sind namespaces prima.
    Dann kannst Du auch die von Dir gewohnten Funktionspointer ("Nicht-member-Pointer") verwenden.
    C++ unterstützt Dich durch overloading und Operatoren ziemlich in dieser Vorgehensweise.

    Also wenn es nach mir ginge würde ich das sicher in keiner eigenen Klasse machen 😃

    Aber ich muss mich ja an meine Angabe halten(Klasse A ist zu vergleichen mit CMathFloat und Klasse B mit CMathFloatFlex):

    Implementieren Sie eine Klasse CMathFloatFlex, welche von der (modifizierten) Klasse
    CMathFloat abzuleiten ist.
    Die Klasse CMathFloatFlex hat folgende Zusatzfunktionaliät zu bieten:
    • Zahlen sollen neben dem Fließkommaformat auch im wissenschaftlichen Format
    <floatzahl> e +/- <exponent> einlesbar sein.
    • Die Klasse soll es erlauben, dass man einstelligen oder zweistellige Funktionen
    mittels Name und Funktionspointer registrieren lassen kann. Im Programm
    mymath2 sind dann mindestens 7 Funktionen zu registrieren, wobei zumindest
    die folgenden enthalten sein müssen: „min(x,y)“, „max(x,y)“, „pow(x,y)“,
    „abs(x)“, „diff(x,y)“, „sqr(x)“ (Quadrat-Funktion), und „sgn(x)“ (Signum). Die
    Klasse soll dann dynamisch prüfen, ob bei einem Funktionsaufruf die passende
    Funktion registriert worden ist. Wenn nicht, soll eine Exception geworfen werden.

    Vielleicht ist nun klarer warum ich so auf den 2 Klassen rumreite 🙄

    LG



  • Hmmm ... vielleicht hab ich's nur nicht gefunden, aber ich lese da nichts davon, dass sin&Co Memberfunktionen sein müssten....
    Da steht nur, dass sie "registriert" werden können müssen (also sowas wie "Funktionspointer in die Map" eintragen ... wie Du es oben schon gemacht hast).

    Ich sehe als "2 Klassen" eigentlich nur eine eigene Repräsentation von Zahlen...

    BTW: Unter C++ gehören auf "freie Funktionen" mit zur Schnittstelle ... wie z.B.

    struct A;
    ostream& operator(ostream&, A const&);
    

    Gruß,

    Simon2.



  • Wieso nicht einfach direkt die mathematischen Funktionen in die Map packen?

    typedef double (*mat_function)(double);
    
    class BaseMath
    {
    public:
        void addFunction(mat_function function, const std::string& name);
        void execute(const std::string& name, double argument);
    private:
        typedef std::map<std::string, mat_function> FunctionMapType;
    
        FunctionMapType m_FunctionMap;
    };
    
    void BaseMath::addFunction(mat_function function, const std::string& name)
    {
        m_FunctionMap[name] = function;
    }
    
    void BaseMath::execute(const std::string& name, double argument)
    {
        FunctionMapType::iterator iter = m_FunctionMap.find(name);
        if(iter != m_FunctionMap.end())
        {
            std::cout << "Result: " << (iter->second)(argument) << '\n';
        }
    }
    

    Oder ggf. eine Hilfsklasse benutzen, die Funktionsmaps verwaltet.



  • Tachyon schrieb:

    Wieso nicht einfach direkt die mathematischen Funktionen in die Map packen?...

    Wäre halt eine zusätzliche Kapsel ... aber ob man die braucht, weiß ich nicht. Das könnte man auch genausogut direkt in CMathFloatFlex reinpacken - immerhin soll diese Klasse lt. Aufgabenstellung für Registrierung und Check zuständig sein (wobei ich immer weniger verstehe, was ein "CMathFloatFlex"-Objekt eigentlich sein soll)...

    Gruß,

    Simon2.



  • Den Sinn der übung weiß ich selber nicht so ganz 😕

    Aber hab jetzt eigenständig folgenden Weg gefunden: Hab alles was rund um dieses registrieren läuft in die abgeleitete Klasse gepackt. Ist zwar nicht so wie ich es machen wollte aber immerhin ist es so einfacher und funktioniert jetzt.

    Danke nochmal, bin auf die idee erst gekommen als ich mir meinen eigenen Forum post durchgelesen habe mit der Angabe nochmal drin.

    LG
    Daniel



  • Simon2 schrieb:

    [...](wobei ich immer weniger verstehe, was ein "CMathFloatFlex"-Objekt eigentlich sein soll)...

    Gruß,

    Simon2.

    Ich habe so im Urin, dass es sich dabei um so eine Art normalen Taschenrechner handeln soll (CMathFloat) der um wissenschaftliche Funktionen erweitert werden soll/kann (CMathFloatFlex). Aber wenn dem tatsächlich so ist, verschließt sich mir etwas der Sinn dahinter, Funktionen anmelden zu können. Ich würde die dann gleich fest in die Basis bzw. die Ableitung(en) einbrennen.



  • Außerdem finde ich es ziemlich merkwürdig, verschiedene Laufzeitflexibilisierungsmechanismen zu mischen (Vererbung und Map) ...

    Gruß,

    Simon2.



  • Simon2 schrieb:

    [...]Laufzeitflexibilisierungsmechanismen[...]

    😮 🤡

    Jo, stimmt.



  • Tachyon schrieb:

    Simon2 schrieb:

    [...]Laufzeitflexibilisierungsmechanismen[...]

    😮 🤡

    Jo, stimmt.

    Hast recht ... schrieb sich ganz entspannt, aber zu lesen ist's ungeschmeidig...

    Gruß,

    Simon2.


Anmelden zum Antworten