Dynamic und Static Cast? oder einfach nur ein normales Cast?



  • hehe oh man du hast recht.. als ich "Langzage " gelesen habe.. hab ich die ganze zeit nich tan prgorammiersprache sonder an sprachauswahl für Menus und so gedacht;) .. deswegen hab ich da nichts reignshcaut

    danke



  • @Boris: Was hältst du von dem Post von HumeSikkins, meinst du nicht das könnte in deinem Fall zutreffen.



  • naja am design will ich nun wirklich nich mehr dran herum schrauben..

    das Ko´nzept sieht so aus:

    es gibt eine Klasse 'AllocationStructure' welche eine 'AllocNode' Knoten verwaltet, diese Knoten werden in einer liste in der 'AllocationStructur' anglegt. Dabei können die Knoten in Form einer Baumstruktur angelegt werden.

    Die AllocNode ist somit der Abstracte Datentyp für die neuen abgeleiten klassen, in unserem Fall B und C.

    wenn ich nun einen Knoten lesen will, wird mit eine AllocNode Zeiger zurückgegeben, über diesen ich nihc auf die Methoden von B und C zurückgreifen aknnt, deswegen der cast.

    Aber ok, dann frage ich mal anderes herum, unter welchen umständen muss denn ein dynamic_cast verwendet werden?? Gibt es Designs, wo man da nich drum herum kommt? Bzw. wo es Designtechnisch nicht anders umsetzbar ist?



  • BorisDieKlinge schrieb:

    wenn ich nun einen Knoten lesen will, wird mit eine AllocNode Zeiger zurückgegeben, über diesen ich nihc auf die Methoden von B und C zurückgreifen aknnt, deswegen der cast.

    Du musst dabei bedenken: warum hast du hier Polymorphie verwendet? Warum erben B und C von A?

    idR erben B und C von A weil sie austauschbar sind. Sprich ich führe ein neues D ein dass von A erbt und ich muss keinen Strich im Code ändern.

    Bei deinem Design jetzt hast du eine fixe Trennung zwischen B und C. Sie sind nicht austauschbar da sie scheinbar spezielle Sachen implementieren. Wozu also beide auf einen Haufen werfen?

    Es gibt Situationen wo man es tun muss, bzw wo es praktisch ist. Aber in der Praxis sind sicher die meisten dynamic_casts einfach nur faulheit. Es ist halt eine einfache Lösung immer dynamic_cast zu verwenden. Man sollte dabei aber daran denken, dass man damit die Polymorphie wegschmeisst und im Prinzip zu altertümlichen switch-tabellen zurück fällt.

    Aber ok, dann frage ich mal anderes herum, unter welchen umständen muss denn ein dynamic_cast verwendet werden?? Gibt es Designs, wo man da nich drum herum kommt? Bzw. wo es Designtechnisch nicht anders umsetzbar ist?

    Ich hatte noch nie eine Situation wo ich einen dynamic_cast gebraucht habe. Ergo: möglich dass du eine dieser sehr seltenen Situationen erreicht hast wo dynamic_cast einfach die beste Lösung ist - aber das ist eher sehr unwahrscheinlich.

    Einen dynamic_cast kann man zB in einer Plugin Architektur brauchen. Es gibt eine Basisplugin Klasse und alle Plugins erben davon. Nun gibt es aber verschiedene unter plugins zB Input und Output Plugins. Ein Plugin kann nun sowohl Input als auch Output oder auch beides sein. Dann kann es uU praktisch sein einmal einen dynamic_cast zu machen um zu sehen was es denn nun wirklich ist.

    Die Frage stellt sich immer: warum B und C auf einen Haufen werfen, wenn man den haufen nachher wieder auftrennen will? (zu deutsch: warum eine list<A*> machen und B und C reinstecken um dann nachher nicht A rauszunehmen sondern wieder nach B und C aufzuteilen? Warum dann nicht gleich eine list<B> und eine list<C>?)



  • BorisDieKlinge schrieb:

    Aber ok, dann frage ich mal anderes herum, unter welchen umständen muss denn ein dynamic_cast verwendet werden?? Gibt es Designs, wo man da nich drum herum kommt? Bzw. wo es Designtechnisch nicht anders umsetzbar ist?

    Kurzfassung: Nur in absoluten Notfällen.

    Etwas ausführlicher: Wenn in deinem Programm Cast-Ausdrücke (egal welche) auftreten, ist das fast immer ein Zeichen für Design-Fehler. Entweder du kennst den korrekten Typ, dann solltest du auch eine dazu passende Variable verwenden (womit der Cast unnötig wird) oder der tatsächliche Datentyp ist irrelevant, dann nimmst du einen Zeiger auf die Basisklasse und arbeitest mit Polymorphie.

    Der einzige Grund für einen Cast ist imho, die Anforderungen irgendwelcher Bibliotheksfunktionen zu erfüllen, die man nicht beeinflussen kann, wie z.B sowas:

    class thread_user
    {
    public:
      void start_thread()
      {
        AfxBeginThread(thread_func,this);
      }
    
      static UINT thread_func(LPVOID me)
      {
        thread_user* pThis = static_cast<thread_user*>(me);
        ...
      }
    };
    


  • CStoll schrieb:

    Der einzige Grund für einen Cast ist imho, die Anforderungen irgendwelcher Bibliotheksfunktionen zu erfüllen, die man nicht beeinflussen kann

    Hmm. Das ist nur eine Facette. Grundsätzlich gibt es einige Fälle, in denen man nicht sinnvoll Typen konkretisieren kann. Ein Beispiel sind z.B. GUI-Bibliotheken, in denen ein Fenster fast immer eine Controls-Collection besitzt, also irgendeinen Container, der Child-Controls enthält. Angenommen, wir wollen nun alle Buttons auf dieser Form durchlaufen und sie anklicken. Der beste Weg dafür ist folgendes:

    for (Iterator<Control> c = form->controls.begin(); c != form->controls.end(); ++c) {
        Button* b = dynamic_cast<Button*>(c);
        if (b) b->perform_click();
    }
    

    Alles andere (z.B. einen Extra-Container für Buttons zu verwalten) verursacht ein enormes Zusatz-Overhead und ist selten praktisch.



  • und auch schlecht erweiterbar. Da ich die VCL verwende, kenne ich diese Situation recht gut. 🙂



  • Das Beispeil von Konrad, ist ungefähr vergleichbar mit meiner Situation... ist also schwer...



  • Shade Of Mine schrieb:

    Die Frage stellt sich immer: warum B und C auf einen Haufen werfen, wenn man den haufen nachher wieder auftrennen will? (zu deutsch: warum eine list<A*> machen und B und C reinstecken um dann nachher nicht A rauszunehmen sondern wieder nach B und C aufzuteilen? Warum dann nicht gleich eine list<B> und eine list<C>?)

    das ist kein haufen, sondern eine kette. was man zuerst vorne anhängt kann man zuerst hinten wieder abholen. allein das könnte der grund sein, warum boris es so macht, wie er's macht.
    🙂



  • pale dog schrieb:

    das ist kein haufen, sondern eine kette. was man zuerst vorne anhängt kann man zuerst hinten wieder abholen. allein das könnte der grund sein, warum boris es so macht, wie er's macht.
    🙂

    Du hast kein Wort von dem verstanden was ich geschrieben habe. Bitte lies es nochmal.

    Ob es eine liste, ein baum, ein dreieck oder ein bunter Hund ist, ist egal. Worum es geht ist: ich schmeisse die Typinformation zum Fenster hinaus und nachher spring ich dann selber aus dem Fenster um sie wieder einzusammeln.

    Da sollte sich sofort die frage stellen: warum wegschmeissen wenn ich dann den Müll druchsuchen muss um es wieder zu finden? Warum nicht einfach nicht wegschmeissen, dann spar ich mir den müll anzufassen.



  • Oder um es kurz zu machen: Man sollte Cast weitgehend vermeiden (Häufig ist ein Cast ein Anzeichen für falsches Design). Wenn man in einer Liste von polymorphen Objekten ständig casten muss ist das meistens ein fehler im Design der Basisklasse.

    cu André



  • Shade Of Mine schrieb:

    Ob es eine liste, ein baum, ein dreieck oder ein bunter Hund ist, ist egal.

    oder ein bleicher hund?
    wie auch immer, manchmal ist das ganz und gar nicht egal!

    Shade Of Mine schrieb:

    Worum es geht ist: ich schmeisse die Typinformation zum Fenster hinaus und nachher spring ich dann selber aus dem Fenster um sie wieder einzusammeln.
    Da sollte sich sofort die frage stellen: warum wegschmeissen wenn ich dann den Müll druchsuchen muss um es wieder zu finden? Warum nicht einfach nicht wegschmeissen, dann spar ich mir den müll anzufassen.

    der vergleich hinkt. wenn es so wäre, dann würde es kein 'dynamic_cast' geben.
    🙂



  • pale dog schrieb:

    Shade Of Mine schrieb:

    Worum es geht ist: ich schmeisse die Typinformation zum Fenster hinaus und nachher spring ich dann selber aus dem Fenster um sie wieder einzusammeln.
    Da sollte sich sofort die frage stellen: warum wegschmeissen wenn ich dann den Müll druchsuchen muss um es wieder zu finden? Warum nicht einfach nicht wegschmeissen, dann spar ich mir den müll anzufassen.

    der vergleich hinkt. wenn es so wäre, dann würde es kein 'dynamic_cast' geben.
    🙂

    Ich weiß, wenn es nach dir ginge, könnte man C++ komplett abschaffen.

    Und der Punkt ist auch nicht, daß man dynamic_cast komplett weglassen sollte. Aber bevor man es anfasst, sollte man sich sehr genau überlegen, ob es denn wirklich notwendig ist (und in mindestens 99% der Fälle ist es tatsächlich günstiger, die notwendigen Typ-Informationen von vornherein bei der Hand zu haben, anstatt sie mühsam rekonstuieren zu müssen).



  • Konrad Rudolph schrieb:

    [...]
    Angenommen, wir wollen nun alle Buttons auf dieser Form durchlaufen und sie anklicken. Der beste Weg dafür ist folgendes:

    for (Iterator<Control> c = form->controls.begin(); c != form->controls.end(); ++c) {
        Button* b = dynamic_cast<Button*>(c);
        if (b) b->perform_click();
    }
    

    Alles andere (z.B. einen Extra-Container für Buttons zu verwalten) verursacht ein enormes Zusatz-Overhead und ist selten praktisch.

    Ob das der Beste Weg ist, sei mal dahin gestellt. Es ist zumindest ein gangbarer Weg.
    Der Code hat mich direkt an eine andere Situation erinnert, wo ein dynamic_cast sinnvoll ist: das azyklische-Visitor-Pattern benötigt in seiner Implementation ebenfalls einen dynamic_cast, hilft gleichzeitig aber dabei dynamic_casts zu reduzieren 😉



  • wieso mühsam? ist der cast-befehl so aufwendig?

    ich dachte wenn ich in meine std::list<A*> B und C speichere, werden ja die typinfos sicher durch polymorhie mitgespeichert.... und könne so einfach gecastet werden..



  • HumeSikkins schrieb:

    for (Iterator<Control> c = form->controls.begin(); c != form->controls.end(); ++c) {
        Button* b = dynamic_cast<Button*>(c);
        if (b) b->perform_click();
    }
    

    Ob das der Beste Weg ist, sei mal dahin gestellt.

    Jetzt hast Du mich neugierig gemacht. Was wäre denn eine bessere Alternative?



  • CStoll schrieb:

    pale dog schrieb:

    Shade Of Mine schrieb:

    Worum es geht ist: ich schmeisse die Typinformation zum Fenster hinaus und nachher spring ich dann selber aus dem Fenster um sie wieder einzusammeln.
    Da sollte sich sofort die frage stellen: warum wegschmeissen wenn ich dann den Müll druchsuchen muss um es wieder zu finden? Warum nicht einfach nicht wegschmeissen, dann spar ich mir den müll anzufassen.

    der vergleich hinkt. wenn es so wäre, dann würde es kein 'dynamic_cast' geben.
    🙂

    Ich weiß, wenn es nach dir ginge, könnte man C++ komplett abschaffen.

    ach nöööö... dann wär's aber ganz schön langweilig hier. 😉



  • Nicht mühsam für dich, aber mühsam hinter den Kulissen.

    Beantworte doch mal die Frage: Wenn B und C so verschieden sind, daß du sie nicht über ein gemeinsames Interface ansprechen kannst - warum machst du dir dann überhaupt die ühe, sie in einer gemeinsamen list<> unterzubringen? Entweder die Klassen sind ähnlich aufgebaut, dann nimmt man den A-Zeiger und ruft dessen (virtuelle) Methoden auf - oder sie haben nichts gemeinsam, dann verwaltet man sie auch unabhängig voneinander.



  • BorisDieKlinge schrieb:

    wieso mühsam? ist der cast-befehl so aufwendig?

    Ja, dynamic_cast ist aufwendig. Man braucht ab und an dynamic_cast, aber man sollte es vermeiden wenn möglich (Und erst recht sollte man zweimal überlegen es beim Interieren über eine große Liste einzusetzen).

    BorisDieKlinge schrieb:

    ich dachte wenn ich in meine std::list<A*> B und C speichere, werden ja die typinfos sicher durch polymorhie mitgespeichert.... und könne so einfach gecastet werden..

    Leider ist das nicht ganz so einfach. Denn A ist die Basisklasse. B und C wissen natürlich das sie auch ein A sind, aber umgekehrt ist der weg nicht ganz so leicht.

    cu André



  • Hallo

    @ Boris :
    Richtig das geht schon. dynamic_cast ist deshalb verpönt weil es eben zur Laufzeit eine Bedingung auswerten muß. Je nach Implementation der RTTI ist das in mehr oder weniger zusätzlichen Taktschritten erledigt, die nicht gebräucht würden wenn der Basistyp ausreicht. Wenn du kein dynamic_cast oder typeid in deinem Programm verwendest kannst du dem Compiler das RTTI sogar ganz ausstellen.

    bis bald
    akari


Anmelden zum Antworten