Böser DownCast
-
Original erstellt von Jester:
@Mastah: Ich glaub TS++ will Gründe sammeln, keine Weisheiten.Ein Grund warum Downcasts böse sind ist der, dass man schnell den Überblick im eigenen Code verlieren kann. Beispiel: Es gibt eine Klasse Mensch. Davon wird abgeleitet Baby und Erwachsener. Man erstellt eine Instanz von Baby und castet sie rauf nach Mensch. An einer anderen Stelle macht man einen Downcast zu Erwachsener und sagt pInstance->Speak. Segfault: Das Baby kann nicht sprechen im Gegensatz zum Erwachsenen.
EDIT: So, jetzt stimmts wieder...[ Dieser Beitrag wurde am 08.07.2003 um 13:48 Uhr von MaSTaH editiert. ]
-
selber schuld. wenn man das macht, dann mit dynamic_cast und auf NULL testen.
oder man weiss genau, was man als letztes in seinen container gepackt hat. das koennte man mit einer assertion absichern.
-
Original erstellt von MaSTaH:
Ein Grund warum Downcasts böse sind ist der, dass man schnell den Überblick im eigenen Code verlieren kann. Beispiel: Es gibt eine Klasse Mensch. Davon wird abgeleitet Baby und Erwachsener. Man erstellt eine Instanz von Baby und castet sie runter nach Mensch. An einer anderen Stelle macht man einen Upcast zu Erwachsener und sagt pInstance->Speak. Segfault: Das Baby kann nicht sprechen im Gegensatz zum Erwachsenen.a) Du hast Downcast und Upcast verwechselt.
b) dynamic_cast würde in dem Beispiel fehlschlagen (Nullpointer oder bad_cast-Exception).
-
Original erstellt von Bashar:
**a) Du hast Downcast und Upcast verwechselt.
**Wieso in dem Beispiel ist ein Up und ein Downcast drin.
EDIT: Aaargh, ja ich sehe es. Moment...[ Dieser Beitrag wurde am 08.07.2003 um 13:47 Uhr von MaSTaH editiert. ]
-
Upcast: Ein Cast zu einer Klasse weiter *oben* in der Hierarchie.
Downcast: Ein Cast zu einer Klasse weiter *unten* in der Hierarchie.Mit dynamic_cast macht man Downcasts.
-
dynamic_cast kann aber auch casts auf die Seite machen
class A { public: virtual ~A () {} }; class B : public A { }; class C : public A { }; class D : public B, public C { }; C &wild_caster (B &b) { return dynamic_cast<C&> (b); } int main () { D d; B &b = d; C &c = wild_caster (b); A &a1 = dynamic_cast<A&> (b); A &a2 = dynamic_cast<A&> (c); }
-
genau diese type switches kann man mit einem visitor vermeiden/vertuschen.
Warum denn gleich schweres Geschütz auffahren? Vor dem Visitor-Pattern stehen erstmal ganz simple virtuelle Methoden als switch-on-type Ersatz.
Alles andere ist meiner Meinung nach extrem Situationsabhängig.
Visitor ist auf jeden Fall eins von den teuren Pattern (Komplexität, Aufwand, Abhängigkeiten...) und es hat sehr klare und beschränkte Anwendungsgebiete (stabile Hierarchie bei ständig neuen Wunschalgos).Ich halte es allerdings auch generell für problematisch ein Pattern vorzuschlagen, *ohne* den konkreten Kontext zu kennen.
Mit dynamic_cast macht man Downcasts
Und crosscasts und wenn man will auch upcasts.
-
Original erstellt von Bashar:
**Upcast: Ein Cast zu einer Klasse weiter *oben* in der Hierarchie.
Downcast: Ein Cast zu einer Klasse weiter *unten* in der Hierarchie.
**ja, so kann ich mir's gut merken.
oben ist da, wo die der baum die wurzel hat.
-
Bäume haben ihre Wurzel doch meistens oben oder nicht? Diese komischen grünen Teile auf dem großen langweiligen Wandbildschirm sind halt ne Ausnahme, was solls ...
@Hume: Cross-Cast? Da kann ich mir kaum was drunter vorstellen ...
-
Cross-Cast? Da kann ich mir kaum was drunter vorstellen ...
Hilft das? http://www.research.att.com/~bs/glossary.html#Gcrosscast
Letztlich z.B. der wild_caster aus davies Beispiel.
[ Dieser Beitrag wurde am 08.07.2003 um 19:15 Uhr von HumeSikkins editiert. ]
-
um virtuelle methoden geht es hier nicht. man will in den unterklassen noch sachen darueber hinaus. man kann ja schlecht die vereinigung aller interfaces schon in die basisklasse tun.
-
Original erstellt von HumeSikkins:
Hilft das?Ja, danke.
-
um virtuelle methoden geht es hier nicht
Huh? Warum nicht?
man will in den unterklassen noch sachen darueber hinaus
Man? Wann? Und was für Sachen?
man kann ja schlecht die vereinigung aller interfaces schon in die basisklasse tun.
Entschuldige aber ich kann dir nicht folgen. Mir scheint du sprichst über ein spezielles Problem das mir nicht bekannt ist.
Mal abgesehen davon, dass es viele Situationen gibt in den ein erweitertes Basisklasseninterface sinn macht (Stichwort: Composite), verstehe ich ehrlich gesagt nicht, wieso du von bösen down_casts automatisch zu Unterklassenerweiterung und Visitor kommst.
-
man wuerde ja nicht downcasten wollen, wen alle das gleiche interface haben.
beim composite gibt es genau 2 dinge zu unterscheiden. selbst das finde ich nicht schoen, aber bei mehr wirds unmoeglich. dann hat man statt der dynamic casts halt isClassA und isClassB und so weiter.
-
man wuerde ja nicht downcasten wollen, wen alle das gleiche interface haben.
Naja, der downcast tritt ja am häufigsten dort auf wo gerade *keine* ordentlichen Interfaces bzw. Hierarchien existieren. Das ist ja genau der Punkt. Häufig ist ein downcast ein Zeichen dafür, dass man Laufzeit-Polymorphie gar nicht oder nicht gut eingesetzt hat.
Wie auch immer, imo sollte man erstmal das Naheliegende besprechen, bevor man irgendwelche Design Patterns rauskramt. Besonders wenn es nicht um ein konkretes Problem geht.
-
Wenns um die Unterscheidung verschiedener Ableitungen geht, ist mir bis jetzt noch kein Fall vorgekommen, wo mans nicht auch mit Polymorphie hingekriegt hätte -> mein Beitrag in Deinem anderen Thread.
Ich kenn das casten nur aus so Fällen, wo man das wissen über den echten Typen eher formal aber nicht wirklich verliert. A la
class DataSet { //Basisklasse für die Klassen die für den Output //eines Datensatzes zuständig sind class Oput { public: virtual bool Write(DataSet&)=0; } }; class Kunde :public DataSet { class Oput :Oput { public: virtual bool Write(DataSet& daset) //DataSet wird nur wg. der formalen Schnittstelle übergeben { //ansonten 'weißt' Du, das Kunde::Oput //nur für Kunden //zuständig ist (dieses Wissen solltest //Du aber mindestens //mit einem assert oder evtl durch //geschickte Vergabe von //zugriffsrechten absichern) Kunde& kunde= static_cast<Kunde&>(daset); //hier reicht schon ein static cast! //jetzt kannst Du auf die speziellen Schnittstellen des Kunden zugreifen } } };
[ Dieser Beitrag wurde am 08.07.2003 um 22:59 Uhr von kartoffelsack editiert. ]
-
ich hab da in einem projekt z.b. die unterscheidung zwischen pflanze und tier, die beide von lebewesen erben.
jetzt gibt es 10 verschiedene pflanzen und 10 verschiedene tiere.
es gibt noch eine klasse, die bestimmte dinge verwaltet, und je nachdem, ob sie eine bestimmte aufgabe erledigen soll, wenn ein tier sie anspricht, oder eine pflanze, soll sie verschiedene interfaces bereitstellen, bzw. ein interface, das sich bei tier oder pflanze verschieden verhält.jetzt gibt es die funktion:
auto_ptr<Interface> Verwalterklasse::Interface (Lebewesen *l) { if (dynamic_cast<Pflanze*>(l)) return auto_ptr<Interface> (new PflanzenInterface); else return auto_ptr<Interface> (new TierInterface); }
es gibt einfach niemals mehr als Tier und Pflanze, da ist ein dynamic_cast doch gerade recht. ich weiß, es ist entweder tier oder pflanze. und um dem wesen das richtige interface bereitzustellen, wird der typ unterschieden.
(ginge normalerweise auch über funktionsüberladung, aber hierfür ist das design zu kompliziert, zumindest komplizierter, als jetzt beschrieben)
was spricht denn hier dagegen?
-
Der Zweizeiler sieht zwar verlockend einfach aus. Das schlechte daran ist aber imho meist ein grundsätzlicher Designfehler: Du verwirfst das Wissen über die Typen, das Du ja anfangs hast und stellst dann später fest, dass Du's trotzdem wissen muss.
Das ist so wie wenn Dein Zooverwalter nur mitzählt, wie viele Lebewesen er in seinem Tierpark hat und dann jedesmal beim Futterkaufen den Tierhüter anruft und ihn nachgucken lässt wie viele Löwen und Bananenbäume es jetzt genau sind.
-
Man könnte auch Lebewesen eine virtuelle Funktion LebewesenInterface* createInterface() verpassen, damit wär aber jede Lebewesen-Unterklasse von ihrer korrespondierenden Interface-Klasse abhängig. Also baut man sich, wenn man das nicht will, eine Factory (wie du), und muß dann eben mit dem Type switch leben.
-
ich erstelle aber jedesmal neue lebewesen mit einer passenden factory und weiß so gut wie nichts über ihre typen, vor allem dann nicht, wenn ich das system erweitern will. es gibt gras, baum, hund, katze, etc. und ich kann noch immer mehr dazu machen.
das einzige, was ich immer sicher weiß, ist, dass es eine Pflanze oder ein Tier ist. Eines von beiden, kein zwischending. Immer.
und es ist ja auch nur sinnvoll, dem interface der pflanze eine funktion move zu verbieten, oder?