Probleme mit Vererbung und Typkonvertierungen
-
Du benutzt hier Abstract*, die keine Schnittstellen für abgeleitete Klassen bieten kann.
Insofern wirst du nicht auf funkB oder funkC zugreifen können, da fehlt dann vorher nen DownCast, aber hierfür musst du auch ermitteln, wer denn wirklich in Abstract* drin steckt.Was versuchst du genau abzubilden/modellieren ?
-
Tachyon schrieb:
Wenn die beiden Klassen sich nicht durch eine abstrakte Klasse vereinen lassen, wieso versuchst Du es dann?
Ich benötige eine Basis, auf der ich die verwaltenden Funktionen konstruieren kann. Sie besitzt Basisfunktionalität, die alle Subklassen besitzen müssen. Erweiterungen sind dann subklassenspezifisch, sodass diese nicht vorher für alle Klassen in der abstrakten Klasse definiert werden können.
KasF schrieb:
aber hierfür musst du auch ermitteln, wer denn wirklich in Abstract* drin steckt.
Das ist kein Problem, dafür gibt es eine Funktionalität in der abstrakten Klasse von der Art int Abstrakt::getTyp()
Als sehr aufwendige aber funktionierende Lösung wäre folgendes:
//in Other.cpp switch(getSingleton()->getObject(2)->getTyp()){ case 0: //für bspw class B dynamic_cast<B*>(getSingleton()->getObject(2))->funktionB(); break; case 1: //für class C dynamic_cast<C*>(getSingleton()->getObject(2))->funktionC(); break;Aber da an vielen verschieden stellen ein solchen Konstrukt von Nöten wäre, sollte das nicht der Weg sein, den ich gehen will.
Edit: Außerdem müsste ich dann auch alle bekannten Klassen inkludieren, da ich in Zeile 4 und 7 explizit die Klassen nennen.KasF schrieb:
Was versuchst du genau abzubilden/modellieren ?
Es gibt ja zum Thema abstrakte Klasse und Vererbung immer das schöne Beispiel mit Fahrzeug als abstrakte Klasse und den erbenden Klassen Auto und Flugzeug. Dann wird immer von Fahrzeug::bewegen() gesprochen und man solle doch in Auto::bewegen() "fahren()" und in Flugzeug::bewegen() ein "fliegen()" einsetzen. Doch mein Problem könnte in diesem Beispiel folgendes sein: Das Auto soll den linken Blinker setzen und das Flugzeug meinetwegen das Fahrwerk einziehen. Das sind Funktionen die nicht in Fahrzeug gehören und da auch keinen Sinn ergeben würden.
Ich hoffe, ich konnte das jetzt verständlich darstellen?!
-
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.