Probleme mit Vererbung und Typkonvertierungen
-
Das sind aber zwei verschiedene Funktionalitäten.
Ein Flugzeug wenn die Steuerklappen setzt dann fliegt es eine Kurve. Ein Auto kann das auch, Lenkrad drehen. Dies liese sich vereinen mit einer Basisfunktionvoid driveTo( Direction dir );Direction (enum) definiert nun Right, Left, Top, Bottom. Bei Top und Bottom wird halt dann beim Auto nix machen/Meldung ausgeben/Exception werfen.
Blinken ist da fehl am Platz. Das ist etwas spezielleres, da das nicht jedes Fahrzeug kann, sollte das nicht in der Basisklasse vorhanden sein.
Bedenke auch, dass man links blinken kann und dann nach rechts fahren

Du kannst dir überlegen, ob du noch ne zusätzliche Klasse "Blinkable" (oder "DirectionChangeIndicatable" oder was auch immer, ist ja wurscht) verwendest, die dann Auto, Boot, eben alles was blinken kann aber keine eigene Basisklasse hat, zusätzlich beerben.
-
Wie wärs mit dem Visitor Pattern?
class Abstract; class B; class C; class Visitor { public: void visit(Abstract &a); void visit(B &b); void visit(C &c); } class Abstract { public: virtual void accept(Visitor &) = 0; }; class B: public Abstract { public: virtual void accept(Visitor &v) { v.visit(*this); } }; class C: public Abstract { public: virtual void accept(Visitor &v) { v.visit(*this); } };Innerhalb der visit()-Methoden des Visitors kennst du jetzt den wahren Typ des Objekts und kannst spezifische Methoden aufrufen.
Stefan.
-
@robb3n,
Schon mal die Lösung in betracht gezogen, die Klassen in getrennte Container zu speichern? Die Anfänger probieren vielfach zu wenig Container einzusetzen und wollen alles in einen stopfen, dabei ging es genausogut, bzw. deutlich einfacher, mit mehreren Containern.Aber um dir dabei zu helfen, es über die getrennten Container zu lösen, müsstest du ein paar Schritte zurück gehen und erklären, was genau du tun möchtest.
Grüssli
-
DStefan schrieb:
Wie wärs mit dem Visitor Pattern?
Das sieht ja voll interessant aus. Ich hab mich dann mal weiter in die Richtung informiert, hab allerdings nicht genau verstanden wie ich das dann in der others.cpp einsetzen könnte. Alle Beispiele die ich gefunden habe sind dann einfach alle durchgegangen aber ich möchte ja explizite Funktionen einzelner KLassen aufrufen. Wie würde es bspw aussehen, wenn ich einmal B::functionB1() aufrufen möchte und an einer anderen Stelle B::functionB2()?
Dravere schrieb:
Schon mal die Lösung in betracht gezogen, die Klassen in getrennte Container zu speichern? Die Anfänger probieren vielfach zu wenig Container einzusetzen und wollen alles in einen stopfen, dabei ging es genausogut, bzw. deutlich einfacher, mit mehreren Containern.
Möglicherweise wäre das sinnvoller, aber ich muss auf dieser Basis aufsetzen, sodass ich aktuell nicht weiter in diese Richtung denken kann.
-
Es ist m.A. nach ein Designfehler, Objekte einer Basisklasse in einer map zusammen zu hauen und dann darauf zu bestehen, auf die spezifischen Methoden ein zu gehen. Abstrakt ist nun mal die Schnittstelle, nur über sie solltest du zugreifen können.
Du kannst eine fette Schnittstelle implementieren, d.h. große Funktionalität in die Schnittstelle packen und dann in den Subklassen prüfen, ob diese diese Funktion unterstützen soll. Wegen virtuellen Funktionen und dynamischer Typenüberprüfung wäre das aber recht unperformant.
-
robb3n schrieb:
DStefan schrieb:
Wie wärs mit dem Visitor Pattern?
Das sieht ja voll interessant aus. Ich hab mich dann mal weiter in die Richtung informiert, hab allerdings nicht genau verstanden wie ich das dann in der others.cpp einsetzen könnte. Alle Beispiele die ich gefunden habe sind dann einfach alle durchgegangen aber ich möchte ja explizite Funktionen einzelner KLassen aufrufen. Wie würde es bspw aussehen, wenn ich einmal B::functionB1() aufrufen möchte und an einer anderen Stelle B::functionB2()?
Der Trick beim Visitor ist, dass du quasi den eigentlichen Typ "zurück gewinnst".
In deiner Map hast du Pointer auf die Oberklasse Abstrakt und rufst über diesen Pointer die (rein) virtuelle Methode accept() auf, deren Argument der Visitor ist. Unterklassen überschreiben accept(), so dass du also durch den Aufruf beispielsweise in B::accept() landest.
Dort wird nun Visitor::visit() aufgerufen und übergibt this (das ein Pointer auf B ist). Da der Visitor visit() für alle infrage kommenden Klassen überladen hat, wird nun also Visitor::visit(B
aufgerufen und - voila - du hast dort den korrekten Typ des Objekts. Dort kannst du dann also Methoden aufrufen, die nur B, nicht aber die Oberklasse Abstrakt besitzt.class Visitor { public: void visit(Abstrakt *obj) { // ... } void visit(A *obj) { obj->method_of_a(); } void visit(B *obj) { obj->method_of_b(); } };Stefan.
-
DStefan schrieb:
class Visitor { public: void visit(Abstrakt *obj) { // ... } void visit(A *obj) { obj->method_of_a(); } void visit(B *obj) { obj->method_of_b(); } };Stefan.
Verstanden. Also ruf ich dann sowas auf (im Vergleich zum Code zum Thread-Start):
// in Others.cpp Visitor v; getSingleton()->getObject(2)->accept(v); //angenommen das Objekt ist Klasse B //in Visitor visit(B &obj) { obj.funktionB1(); }Wie verhalte ich mich dann, wenn ich unterschiedliche Funktionen in den Klassen habe. Soll ich dann immer noch ein Integer der Aufgabe übergeben und #defines einsetzen? So wie hier bspw.:
#define FKT_B1 23 #define FKT_B2 42 // in Others.cpp Visitor v; getSingleton()->getObject(2)->accept(v,FKT_B1); //angenommen das Objekt ist Klasse B //... getSingleton()->getObject(2)->accept(v,FKT_B2); //in Visitor visit(B &obj, int toDo) { switch(toDo) { case FKT_B1: obj.funktionB1(); break; case FKT_B2: obj.funktionB2(); break; } }Gruß robb3n
-
robb3n schrieb:
Wie verhalte ich mich dann, wenn ich unterschiedliche Funktionen in den Klassen habe. Soll ich dann immer noch ein Integer der Aufgabe übergeben und #defines einsetzen? So wie hier bspw.:
#define FKT_B1 23 #define FKT_B2 42 // in Others.cpp Visitor v; getSingleton()->getObject(2)->accept(v,FKT_B1); //angenommen das Objekt ist Klasse B //... getSingleton()->getObject(2)->accept(v,FKT_B2); //in Visitor visit(B &obj, int toDo) { switch(toDo) { case FKT_B1: obj.funktionB1(); break; case FKT_B2: obj.funktionB2(); break; } }Gruß robb3n
Nein, das würde ich nicht so machen. Oder wenigstens nur in Ausnahmefällen. Stattdessen würde ich eine abstrakte Visitor-Klasse machen und für die gewünschten Fälle Unterklassen davon. Auf diese Weise macht jede Visitor-Klasse genau einen Job.
Stefan.
-
Vielen Dank für eure tatkräftige Unterstützung.
Besonderer Dank gilt DStefan für sein Fachwissen.
greetz robb3n
-
robb3n schrieb:
Vielen Dank für eure tatkräftige Unterstützung.
Besonderer Dank gilt DStefan für sein Fachwissen.
greetz robb3n
Kommt alles aus diesem Buch:
Solltest du vielleicht mal lesen.
Aber Vorsicht! Vermutlich wirst du es nicht mehr aus der Hand legen können, bis du damit fertig bist. So ist es mir jedenfalls damals ergangen.

Stefan.