Funktions Pointer + Vererbung = Problem
-
Hallo,
/* Problem */ #include <iostream> using namespace std; class A{ public: A(); virtual ~A(); typedef void ( A::*method_ptr )( ); void mymain(); void addFunction(method_ptr Obj); }; class B: virtual public A{ public: B(); void y(); void addFunction2(); }; int main(){ B myB; myB.addFunction2(); } A::A(){} A::~A(){} B::B(){} void A::addFunction(method_ptr Obj){ // Hier soll der Pointer gespeichert werden } void B::addFunction2(){ addFunction(&B::y); } void y(){}Dieser Code lässt sich nicht kompelieren. Es kommt der selbe Fehler raus wie bei uns:
..\testx.cpp: In member function
void B::addFunction2()': ..\\testx.cpp:38: error: no matching function for call toB::addFunction(void (B::)())'
..\testx.cpp:32: note: candidates are: void A::addFunction(void (A::)())Wie muss ich das machen das ich einen Pointer in Klasse B in diese Speicherfunktion packen kann?
Kurz eine Erklärung warum ich das überhaupt möchte. Ich habe eine Klasse A. Diese Klasse besitzt eine methode um eine neue Funktion einzubinden. Dazu muss allerdings ein Funktionspointer übergeben werden.
Jetzt soll ich eine neue Klasse B machen die von A abgeleitet sein soll. In dieser Klasse B sollen zusätzlich neue Funktionen eingebunden werden. Also schreib ich diese Funktionen und soll diese Funktionspointer dann natürlich auch über diese Methode in A speichern.Hoffe ihr versteht wo mein Problem liegt und könnt mir helfen. Ich wär sehr dankbar dafür!
Grüße
-
Funktion einzubinden. Dazu muss allerdings ein Funktionspointer übergeben werden.
Wie meinst du das mit "einbinden"?
Das Problem ist, was der Compiler dir auch mitteilt, dass du einen Funktionszeiger auf A haben musst, du aber übergibst einen auf B.
Da kannst du was mit templates machen.
-
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));
-
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'
-
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.