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



  • GreyHound schrieb:

    Der dynamic_cast Operator arbeitet nur mit Zeigern auf polymorphe Klassen, d.h. die Klasse muss mindestens eine virtuelle Memberfunktion enthalten.

    Geht diese Aussage zusammen mit dem was ihr 2 gerade beschrieben habt? Der Cast kann ja 100% richtig sein nur nach obigem quote nicht möglich wegen fehlender virtueller methoden?

    Wenn die Klassen nichts virtuelles (und damit keine RTTI) haben, wird ein dynamic_cast schon vom Compiler abgewiesen. Nicht richtig an dieser Aussage ist aber, dass der dynamic_cast nur mit Zeigern funktioniert, denn er funktioniert genausogut mit Referenzen auf polymorphe Klassen. Schlägt der Cast dann fehl, wird nicht 0 zurückgegeben, sondern std::bad_cast geworfen.



  • Hallo,
    die technischen Details wurden von CStoll und LordJaxom ja schon genannt. Bevor du jetzt aber mit dem wilden Casten beginnst, solltest du dir noch mal das Design durch den Kopf gehen lassen.

    Konkret:
    warum verwendest du eine std::list<A*> für die Speicherung von Bs und Cs, wenn A, B und C nicht austauschbar sind? Wenn B und C wirklich so unterschiedlich sind, dass eine gemeinsame Basis A nicht ausreicht, dann verwende besser zwei Listen - eine für Bs und eine für Cs. Andernfalls überprüfe die Abstraktion A. Insbesondere: was musst du tun, damit B und C sinnvoll (also transparent) über die Abstraktion A verwendet werden können?

    Erst allles in eine std::list<A*> packen (= Typinformation wegschmeißen) und dann nachher beim Auslesen casten (= Typeinformation wiederherstellen) ist selten der richtige Weg.



  • ok gut das hab ich alels verstanden, wenn ich nun mit dynamic cast arbeite kommt die warnung:

    c:\test\AllocStruct\Transfer.h(78): warning C4541: 'dynamic_cast' used on polymorphic type 'CAllocation::CAllocNode' with /GR-; unpredictable behavior may result
    

    und mein prgoramm stürtz ab

    und 'CAllocation::CAllocNode' ist abstract



  • Dann les doch mal die Fehlermeldung...

    warning C4541: 'dynamic_cast' used on polymorphic type 'CAllocation::CAllocNode' with /GR-;

    Jetzt mal völlig unwissend und auf doof geraten: Du musst statt /GR- /GR+ setzen... 🙄



  • sorry... bin vll. noch nich so involliert? was ist GR- bzw. GR+ ne compitler eingstellung ?? wo stell ich das ein? habe VS 6.0



  • LordJaxom schrieb:

    Dann les doch mal die Fehlermeldung...

    warning C4541: 'dynamic_cast' used on polymorphic type 'CAllocation::CAllocNode' with /GR-;

    Jetzt mal völlig unwissend und auf doof geraten: Du musst statt /GR- /GR+ setzen... 🙄

    Gut geraten 😉 Die Fehlermeldung besagt, daß du das Programm ohne RTTI übersetzt hast (da dynamic_cast auf RTTI basiert, kann das so nicht funktionieren). Das heißt, du mußt die RTTI einschalten, indem du /GR an den Compiler übergibst (für MSVC: Projekt->Einstellungen->Tab "C/C++"->Kategorie "Programmiersprache C++"->ein Häkchen vor "RTTI aktivieren").



  • ich komm bis "C/C++" aber ne kathegorie gibts nich... bin alles durchgegangen kann auch kein RTTI finden...

    P.S.: hab die englische version...



  • Auf Englisch heißt das dann vermutlich "category" (ist eine Listbox ziemlich weit oben in dem Dialog).



  • das ist ne combo box wo ich die plattform "win32" oder "(active)win32" auswäghen kann, und dem knoten "C/C++" gibts
    - General
    - Optimation
    - Preporcessor
    - Code Generation
    - Language
    - Precompiled Header
    - Output files
    - Brows Informations
    - Advanced
    - Command line

    ?????

    Kategorie gibts net....



  • Da scheint das doch grundsätzlich anders aufgebaut zu sein als in der deutschen Version. Wenn ich raten müsste, würde ich mal auf 'Language' tippen.

    (alternativ tippst du das /GR einfach von Hand in den Options-String ein ;))



  • 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.


Anmelden zum Antworten