Konversion Basisklasse nach abgeleitete Klasse
-
Hallo,
folgende Situation: Ich habe eine Basisklasse (B), von der ich drei Klassen (U, V, W) ableite. Nun entwerfe ich eine weitere Klasse T, die grundsätzlich für jede der Klassen U, V und W funktionieren soll. Dafür würde ich der Klasse jedoch gerne nur einen Konstruktor und eine Membervariable geben. Geht das mit Verwendung der Klasse B als "by reference"-Argument im Konstruktor? Wenn ich den Konstruktor dann mit einer der abgeleiteten Klasse aufrufe, kann ich dann trotzdem noch auf deren Spezialisierungen zugreifen?
Alternativ müsste ich halt jeweils drei Konstruktoren und drei Membervariablen, je eine vom Typen U, V und W verwenden ...
Gruß,
TheBrain
-
Bitte ein wenig Code, damit man weiss, was du damit meinst:
Nun entwerfe ich eine weitere Klasse T, die grundsätzlich für jede der Klassen U, V und W funktionieren soll.

-
Habe noch keinen Code, bin noch in der Entwurfsphase. Aber ich versuche das Beispiel zu präzisieren:
Ich habe eine Klasse Auto. Davon leite ich drei weitere Klassen ab: Trabant, Porsche und Opel (auf konkrete Modelle sei an dieser Stelle verzichtet ...).
Jetzt will ich eine Klasse Werkstatt, die als Membervariable ein Objekt der Klasse Auto hat. Für den Konstruktor, der u. a. diese Membervariable initialisiert, möchte ich aber z. B. auch "Werkstatt(Opel)" angeben können, ohne , dass mir dann die speziellen Eigenschaften der Klasse Opel verloren gehen, da die Membervariable ja vom Typ Auto ist.
Funktioniert so etwas grundsätzlich? Also Konversion von Objekt der abgeleiteten Klasse in Objekt der basisklasse ohne Informationsverlust?
Ich hoffe, das ist etwas verständlicher.
-
Willst du eine abstrakte Basisklasse?
class Auto { public: virtual void fahre () = 0; virtual void halten () = 0; }; class Porsche : public Auto { public: void fahre (){} void halten (){} }; class Werkstatt { Auto* a; public: void SetAuto (Auto* _a){a = _a;} void LassAutoFahre (){a->fahre ();} };Funktioniert so etwas grundsätzlich? Also Konversion von Objekt der abgeleiteten Klasse in Objekt der basisklasse ohne Informationsverlust?
Ja das ist möglich, aber nicht sehr schön. Da musst du eher das Design überdenken, wenn du es für nötig befindest einen einen downcast zu machen.
-
Aber was wäre die Alternative? Ich will halt nicht in der Werkstatt-Klasse für jede mögliche von Auto abgeleitete Klasse eine Membervariable und einen Konstruktor haben müssen.
-
Das Konzept nennt sich Polymorphie. Die Basisklasse muss nicht unbedingt
abstrakt sein.Du kannst dann die Methoden "fahre" und "halten" auf der Membervariable
Werkstatt::a aufrufen und es wird zur Laufzeit entschieden, welches Objekt jetzt
"fährt" oder "hält".Wieso soll ein Down-Cast schlecht sein? Aus Performance-Gründen?
\edit:
Vielleicht meint er, dass du statt Klassen "Opel" usw. die Klasse "Auto" um
eine Membervariable erweiterst, die anzeigt, von welchem Fahrzeughersteller es
ist.
-
Es geht mir um die speziellen Eigenschaften der abgeleiteten Klassen. Gehen diese verloren, wenn ich einen "Downcast" mache?
-
Nein.
(Wenn du es richtig anstellst
)
-
Und worauf genau muss ich achten? Welchen Typ muss die Membervariable der Werkstatt-Klasse haben? Auto*? Und wie würde der Konstruktor aussehen? Werkstatt(Auto* a)?
Dann könnte ich der Auto-Klasse noch ein Member geben, das zu Beginn festhält, um welchen speziellen Typen (Trabant, Opel, Porsche) es sich handelt und in den Werkstatt-Funktionen kann ich dann jeweils per switch o. ä. die entsprechenden Funktionen aufrufen. Oder gibt es einen geschickteren Lösungsweg?
-
Also enweder so:
TheBrain schrieb:
Dann könnte ich der Auto-Klasse noch ein Member geben, das zu Beginn festhält, um welchen speziellen Typen (Trabant, Opel, Porsche) es sich handelt und in den Werkstatt-Funktionen kann ich dann jeweils per switch o. ä. die entsprechenden Funktionen aufrufen. Oder gibt es einen geschickteren Lösungsweg?
und das ganze ohne Polymorphie.
Oder du nimmst Polymorphie und lässt den Rechner entscheiden, welches Auto er
gerade fährt
Wenn du den 2ten Ansatz nimmst, dann musst du in der Basisklasse
"Auto" jede Methode, die von einer abgeleiteten Klasse genutzt werden
soll (auch wenn es nur 1 Klasse ist), als virtuelle Methode einbinden. So wie es
drakon schon gezeigt hat ohne das " = 0", außer du willst keine Instanzen von
"Auto" erzeugen und nur mit den abgeleiteten Klassen arbeiten.
-
Warum musst du in der Werkstadt denn wissen welches Automodell gerade eingeliefert wurde?
Kannst du das Detail-Wissen, also das modell-spezifische Wissen, nicht in die Auto-Klasse verlagern bzw. einen "Opel-Experten" einführen, welcher diese spezifischen Aufgaben beherrscht, und die Werkstatt holt sich, dann über eine Factory den zugehörigen Experten bzw. über eine Methode der Opel-Klasse.
-
Chuck schrieb:
und das ganze ohne Polymorphie.
Was in diesem Fall konkret bedeutet?
Chuck schrieb:
Oder du nimmst Polymorphie und lässt den Rechner entscheiden, welches Auto er
gerade fährt
Wenn du den 2ten Ansatz nimmst, dann musst du in der Basisklasse
"Auto" jede Methode, die von einer abgeleiteten Klasse genutzt werden
soll (auch wenn es nur 1 Klasse ist), als virtuelle Methode einbinden. So wie es
drakon schon gezeigt hat ohne das " = 0", außer du willst keine Instanzen von
"Auto" erzeugen und nur mit den abgeleiteten Klassen arbeiten.Ich will ja schon eine Instanz von Auto erzeugen, eben gerade in der Klasse Werkstatt, als generischen Platzhalter für Trabant, Opel oder Porsche.
Hab ich das richtig verstanden, ich muss jede spezielle Methode der Unterklassen auch mit in die Basisklasse aufnehmen? Das wäre ja obstrus ...
-
Tippgeber schrieb:
Warum musst du in der Werkstadt denn wissen welches Automodell gerade eingeliefert wurde?
Kannst du das Detail-Wissen, also das modell-spezifische Wissen, nicht in die Auto-Klasse verlagern bzw. einen "Opel-Experten" einführen, welcher diese spezifischen Aufgaben beherrscht, und die Werkstatt holt sich, dann über eine Factory den zugehörigen Experten bzw. über eine Methode der Opel-Klasse.Weil ich in der Werkstatt je nach Automodell verschiedene Funktionen aufrufen können möchte.
Ich suche wirklich nur einen Weg, zu verhindern, dass ich für jedes Automodell eine eigene Membervariable und einen eigenen Konstruktor einführen muss.
-
Also.
Auto habe ich abstrakt gewählt, weil es schlichtweg unsinnig ist ein Auto ohne Marke anzubieten.
Jedes Auto hat eine Marke. Ob das jetzt Porsche, Audi, Golf, Smart oder sonst was ist spielt ja keine Rolle. Aber ist das wichtig zu wissen, welche Marke ein Auto hat, wenn man es in die Werkstatt bringt? (jetzt mal davon ausgegangen, dass eine Werkstatt alle Marken reparieren kann). Nein. Du kannst grundsätzlich bei jedem Auto die Pneus wechseln, du kannst jedes Auto starten, abschalten, die Scheibenwischer laufen lassen usw.
Und diese Funktionaltität gehört zu einem Auto, wo du auch die virtuellen Funktionen schreibst.
Was es im Endeffekt bedeutet, dass du die Scheibenwischer laufen lässt, kannst du ja selber implementieren in der abgeleiteten Klasse. Das ist aber für eine anstrakte Klasse Auto egal. Er muss nur wissen, wie man die laufen lässt.
Mir fällt nichtmal ein fiktives Beispiel ein, wo ein Porsche sich anders behandeln lässt, als ein Audi. Mal abgesehen von Preis, Aussehen, Geschwindigkeit usw., macht es ja überhaupt keinen Unterschied, ob ich jetzt einen Porsche, oder einen Audi fahre.Wieso soll ein Down-Cast schlecht sein? Aus Performance-Gründen?
Nein, ist er nicht grundsätzlich nicht, aber hier (wahrscheinlich) fehl am Platz. Es ist gar nicht nötig einen down-cast durchzuführen.
Mal angenommen, dass du jetzt eine Garage willst, welche NUR Porsche aufnehmen kann wäre eine Implementierung durch ein down-casts möglich.
void nehme_auto_auf (auto* a) { if (dynamic_cast<porsche*> (a)) { autos.einfügen (a); } }Aber selbst hier finde ich einen down-cast unsinnig. Was ist den jetzt, wenn du auf die Idee kommst, dass die Werkstatt ja allgemein Luxusautos aufnehmen kann.
Dann kannst du den ganzen Code anpassen und mit if-Orgien übersähen.
Da finde ich es praktischer, wenn man den Typ einfach mittels eines strings ermitteln kann und dann kann man ganz leicht einen Vergleich machen mit dem, was die Werkstatt aufnehmen kann.
-
Ich sehe schon, das Beispiel war wieder schlecht gewählt ... Ich bräuchte nämlich tatsächlich eine Werkstatt, die Automodell-spezifische Operationen durchführen kann. Wie gesagt, mein Problem lässt sich darauf reduzieren:
Ich suche einen Weg, zu verhindern, dass ich für jedes Automodell, das von der Werkstatt behandelt werden kann, eine eigene Membervariable und einen eigenen Konstruktor einführen muss. Die Werkstatt soll stattdessen auf einem allgemeinen Auto-Objekt arbeiten, intern jedoch wissen, um welches Modell es sich dabei handelt, um die Automodell-spezifischen Funktionen aufrufen zu können.
Jedenfalls sieht das dynamic_cast genau nach dem aus, was ich brauche, damit werde ich es morgen mal probieren! Danke!
-
@TheBrain
Naja dein Ansatz ist sehr praxisbezogen und kollidiert halt mit den OOP Grundsaetzen ein bisserl.Ne werkstatt die auf grund eines spezifischeren Types einer generelleren Basisklasse Entscheidungen (Opartionen) bestimmt, ist halt aua ...
Irgendwie muesstest du deine Besonderheiten der Automarke mit an das eigentliche Object knuepfen ...
Eine deinem bisherigem ansatz naheligendere version, waere die "Automodell-spezifische Operationen" ueber nen Datentyp (vielleicht strings) zu abzuwandeln, und der generischen klasse ne methode mitzugeben, die dir ne liste aller Operationen liefert. die "Werkstatt" fragt diese operationen dann ab, schaut welche sie durchfuehren, parametrisiert vielleicht noch bissi (Preiss vielleicht ?) was, und fuehrt die dann aus ....
Worum geht es bei deinem Projekt eigentlich genauer ... nen richtigen Ansatz zu finden ist so schwer ...
Ist vielleicht das Auto selber gar keine geeignete Hauptklasse fuer deinen Zweck, sondern dreht sich alles um Listen von Serviceauftraegen, die nur Automarkenspezifisch generiert werden ???P.S:
Downcast ist fast immer ein zeichen von schlechtem Design. Bei gutem design braucht man eigentlich sowas nicht ...Ciao ...