Fortschrittsanzeige



  • Geh in den Index und gib "dynamic_cast" ein.



  • dynamic_cast -> definiertes verhalten

    lässt sich auch besser lesen als c Style casts 🤡

    Ich denke ich werd mich da selbst mal durchackern müssen.
    Wenn ich das genau weiß poste ich das mal, ich arbeite zu nahe am C++
    Standard als das wir auf einen Nenner kommen können 😕

    Eventuell weiß Rene noch die Antwort darauf, wieso die laufzeit nicht vorhanden ist.... Schaut nach größerer Quelltextsucherei aus .

    thx @ll



  • warum funktioniert dynamic_cast denn nicht. ist der zeiger dannach NULL oder wird eine bad_cast Exception geworfen?



  • Da ich aber auch nicht wirklich erkennen kann, wozu dynamic_cast wirklich gut sein soll, bzw. ob an dem Ausdruck

    A* pA = dynamic_cast<A*>(pB);
    irgendetwas besser ist, als bei

    A* pA = (A*)pB;
    wüsste ich nicht warum ich ersteren verwenden sollte. Zumal die zweite Form deutlich kürzer ist

    Oha. Das du die Eigenschaften und Vorteile von dynamic_cast nicht kennst ist aber schade (und für mich auch etwas errschreckend).

    dynamic_cast ist der *einzige* geprüfte Laufzeitcast in C++. dynamic_cast ist der *einzige* cast in C++ der ein sicheres Navigieren in komplexen Klassen-Hierarchien erlaubt (also Hierarchien mit mehrfacher und virtueller Vererbung).

    Prinzipiell wird der dynamic_cast benutzt um Basisklassen-Referenzen/Zeiger in Referenzen (Zeiger) auf eine abgeleitete Klasse zu casten (down-cast). Um den umgekehrten Weg zu gehen (up-cast) oder um zwischen Referenzen/Zeigern zweier benachbarten abgeleiteter Klassen hin un her zu casten (sibling-cast)

    Außerdem kann jeder Pointer nach void* gecastet werden.
    Ein solcher Cast liefert einen void-Zeiger auf das am weitesten abgeleitete Objekt der Hierarchy.

    Der dynamic_cast kann dabei nur auf Pointer/Referenzen die Objekte von polymorphen Klassen verweisen angewendet werden. Als Teil des C++ RTTI Systems
    prüft der cast zur Laufzeit die Gültigkeit des Ausdrucks.
    Wird z.B. eine Basisklassenreferenz in eine Referenz vom Typ abgeleitete Klasse gecastet, obwohl das referenzierte Objekt nicht von diesem Typ (oder spezieller) ist, so wird eine bad_cast-Exception geworfen.
    Für Pointer wird der Nullpointer geliefert. Das ist ein sehr wichtiger Unterschied zum C-Cast. Dieser bietet keine Möglichkeit einen ungültigen Cast zu erkennen.

    Du solltest dir am Besten generell noch mal die vier neuen C++ Cast-Operatoren anschauen. Diese bieten erhebliche Vorteile gegenüber dem alten one-size-fits-all C-Cast (auch als Vorschlaghammer bekannt). Jeder einzelne C++ Cast hat sein wohldefiniertes Einsatzgebiet. Und nur diese Casts halten sich z.B. auch an die in C++ definierten Access-Level.

    Der einzige Nachteil der C++ Casts im Gegensatz zum C-Cast ist die Länge. Ich sehe das aber nicht als Nachteil. Erstens lassen sie sich dadurch besser finden und zweitens hast du in der Zeit in der du den Cast-Operator eintippst nochmal die Zeit darüber nachzudenken, ob ein Cast wirklich der richtige Weg ist.

    Einen kleinen Einführungsartikel zum Thema C++ Casts findest du hier: http://www.acm.org/crossroads/xrds3-1/ovp3-1.html



  • Also ich find's weder schade noch erschreckend. Es gibt sicherlich Anwendungen wo dynamic_cast etc. notwendig/ sinnvoll sind. Aber ebenso gibt es genug Anwendungen wo die "billige" Version ebenso sinnvoll ist. Wenn ich definitiv weiß, dass ein per CWnd* pWnd gelieferter Zeiger eigentlich vom Typ CMyView* ist, dann brauche ich das nicht noch während der Laufzeit zu überprüfen. Und ich hab bisher nie, während irgendeiner Programmentwicklung, beim debuggen feststellen müssen, das irgendein "gecasteter" Pointer gar nicht das passende Objekt ist.



  • Wenn ich definitiv weiß, dass ein per CWnd* pWnd gelieferter Zeiger eigentlich vom Typ CMyView* ist

    Aber CWnd::GetDlgItem liefert einen CWnd-Zeiger zurück und keinen z.B. CMyView*. Der CMyView Konstruktor würde nie aufgerufen und wenn CMyView noch irgendwelche zusätzlichen Variablen hat, werden diese nicht vernünftig initialisiert.



  • Die Diskusion haben wir weiter oben schon durch 🤡

    Bei dem GetDlgItem sehe ich zwei möglichkeiten:

    1. es gibt ein CWnd das ein HWND zugewiesen bekommt. Somit war das CWnd NIE ein MFC Control. Das der Aufruf für Methoden ohne zugriff auf this oder membervariablen funktioniert hat Rene für den VC weiter oben gezeigt. Dem C++ Standard nach gibt das undefiniertes verhalten. (s.o.)

    2. Das CWnd was zurück gegeben wird aus der HandleMap ist das CWnd was in der Vererbungslinie des MFCControls steht. Und da sollte dann der dynamic_cast funktionieren. Ich sehe den Grund nicht warum die RTTI versagen sollte oder fehlen sollte.



  • Die MFC wurde vielleicht nicht mit RTTI Unterstützung kompiliert?



  • Nope, extra noch mal überprüft. RTTI is aktiv:(



  • Also ich find's weder schade noch erschreckend

    Schade, da *nur* dynamic_cast dir ermöglicht sicher durch jede Form von Hierarchie zu navigieren und man als Programmierer irgendwann in seinem Leben jede Form von Hierarchie sehen wird. Es wäre also schade, wenn du später an eine komplizierte Hierarchie kommst und den dynamic_cast nicht kennst.
    Erschreckend, da *nur* dynamic_cast dir ermöglicht sicher durch jede Form von Hierarchie zu navigieren und du offenbar C++ mit Vererbung einsetzt. Erschreckend also einfach aus Sicherheitsgründen. Erschreckend ist aber zugegebenermaßen ein zu hartes Wort.

    Aber ebenso gibt es genug Anwendungen wo die "billige" Version ebenso sinnvoll ist.

    Nenn mir außer der Sache mit dem geringeren Tippaufwand bitte einen.

    Wenn ich definitiv weiß, dass ein per CWnd* pWnd gelieferter Zeiger eigentlich vom Typ CMyView* ist, dann brauche ich das nicht noch während der Laufzeit zu überprüfen.

    Wenn ich das definitiv weiß und wenn ich eine Hierarchie ohne multiple und virtuelle Vererbung habe, dann nehme ich natürlich static_cast. Dazu ist dieser Cast-Operator schließlich u.A. da.

    Beinhaltet meine Hierarchie mehrfache bzw. virtuelle Vererbung, dann ist der C-Cast genauso undefiniert wie der static_cast und es bleibt einem *nichts* anderes als ein dynamic_cast.

    [ Dieser Beitrag wurde am 30.03.2003 um 19:43 Uhr von HumeSikkins editiert. ]



  • @cole:

    Wenn ich in meinem Programm ein Objekt von CMyView erzeugt habe und es über CWnd::GetDlgCtrl() mir liefern lasse, dann kann ich ohne Probleme folgendes machen:

    CMyView* pView = (CMyView*)(GetDlgCtrl(ID_MYVIEWID));

    Das gibt weder Probleme, noch sonst irgendetwas, denn hinter dem Zeiger verbirgt sich ja schliesslich ein Objekt vom Typ CMyView. Und da ich das ja mal erzeugt habe, wurden auch alle Member-Variablen initialisiert.

    Wenn ich eine Klasse von CWnd abgeleitet habe, aber keine zusätzlichen Member-Variablen hinzugekommen sind, dann ich ohne Probleme jeden Zeiger auf ein CWnd in einen Zeiger dieser abgeleiteten Klasse umwandeln. Zwar nicht wirklich die feine Art, aber im Falle des CProgressCtrl (und darum geht es in dieser Diskussion ja ursprünglich) kann ich das guten Gewissens machen. Denn das CProgressCtrl fügt KEINE neuen Member-Variablen hinzu, es muss also nichts initialisiert werden, ich kann aber die Member-Funktionen verwenden, um die Nachrichten an das Control schicken (was ja nun auch ein ProgressCtrl ist), um Eigenschaften zu lesen bzw. zu setzen. Für den Fall, dass das Fenster doch kein CProgressCtrl ist, ist das auch nicht schlimm, da die Nachrichten halt einfach nicht verarbeitet werden.

    @Knuddlbaer, @HumeSikkins:

    Wenn man der Meinung ist, strikt nach C++-Standard programmieren zu müssen, dann bitte - ist jedem selbst überlassen. Da ich static_cast bisher (ebenso wie dynamic_cast) nicht kannte, hätte ich einige Beispiele nennen können, aber offensichtlich sind diese beiden Zeilen identisch!?:

    BOOL CMyWnd::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
    {
      NMHDR* pInf = (NMHDR*)lParam;
    
      // oder
    
      NMHDR* pInf = static_cast<NMHDR*>(lParam);
    }
    

    Also ich bin mit meinen Feld-Wald-und-Wiesen-Cast 😉 bisher gut über die Runden gekommen, alle Programme laufen problemlos schnell und zuverlässig. Nichts für ungut, aber sollte ich den dynamic_cast benötigen werde ich eine www.c-plusplus.net-Gedenkminute einlegen. 😉



  • Also bevor ich gesteinigt werde: Hab einiges gelernt in diesem TOpic 🤡

    Das gibt weder Probleme, noch sonst irgendetwas, denn hinter dem Zeiger verbirgt sich ja schliesslich ein Objekt vom Typ CMyView. Und da ich das ja mal erzeugt habe, wurden auch alle Member-Variablen initialisiert.

    Ja das dachte ich auch, das es sich um das CWnd handelt. Das schrieb Rene auch ziemlich am Anfang als ich mit dem Standard anfing. Aber ich verstehe nicht warum es dann nicht mit dynamic_cast klappt ?!

    Das würd ich gern noch verstehen.

    [ Dieser Beitrag wurde am 30.03.2003 um 23:06 Uhr von Knuddlbaer editiert. ]



  • Jo, Shaggy, kann ich dir nur zustimmen. 🙂 Hab mich nicht deutlich ausgedrückt.

    Ich meinte es für den Fall, wenn das Control nicht von den MFC erstellt wurde. Dann befindet sich in der Handle-Map kein Eintrag und dann wird ein neues Objekt vom Typ CWnd erstellt, in die Temporary-Map eingetragen und ein Zeiger darauf zurückgeliefert. Und diesen CWnd* wieder in ein CProgressCtrl* oder ähnliches zu casten, kann unter Umständen gefährlich sein.



  • Wenn man der Meinung ist, strikt nach C++-Standard programmieren zu müssen, dann bitte - ist jedem selbst überlassen

    Dieser Meinung bin ich ganz und gar nicht. Danke. Aber selbst wenn. Das eine hat mit dem anderen überhaupt nichts zu tun.

    aber offensichtlich sind diese beiden Zeilen identisch!?:

    Das Wort offensichtlich ist hier schlichtweg falsch. Und wenn du dir die Mühe machst und dich ein wenig mit den C++ Cast auseinandersetzt, dann wirst du auch wissen warum.

    Also ich bin mit meinen Feld-Wald-und-Wiesen-Cast 😉 bisher gut über die Runden gekommen, alle Programme laufen problemlos schnell und zuverlässig. Nichts für ungut, aber sollte ich den dynamic_cast benötigen werde ich eine www.c-plusplus.net-Gedenkminute einlegen. 😉

    Also dazu fällt mir nur noch Hobbes:
    "Why waste time learning when ignorance is instantaneous?"
    ein.

    Es ist sicher jedem selbst überlassen wie er programmiert. Wer sich aber weigert neues zu lernen und dabei auch mal alte Wege zu verlassen, wird meiner Meinung nach irgendwann auf einem Abstellgleis enden.
    Wie schon gesagt. Ich will damit nicht sagen, dass du von nun an immer die neuen Casts verwenden sollst. Ich will damit nur sagen, dass du zumindest wissen solltest wozu man sie einsetzt und wo die Vorteile sind.



  • Ich benutze (shame on me 😉 ) ebenfalls den C-Cast, da ich zu faul bin, erstens etwas dazuzulernen und zweitens einen Mehraufwand an Tipparbeit einzugehen. Aber ich nenne mich selber einen Hobby-Programmierer. Ich werde nie ein richtiger werden, denn um einer zu werden müsste ich sooo viel lesen. Und dazu bin ich ebenfalls schlichtweg zu faul. 😉



  • Nochmal zur Frage, warum dynamic_cast nicht klappen kann:

    Ich weiss nicht genau, wo im Speicher, aber eines ist klar:
    Die Laufzeitinformationen, welche bei GetDlgItem zurückgeliefert werden:
    CObject::CWnd

    Ein dynamic_cast auf CProgressCtrl würde voraussetzen, dass es sich bei den Laufzeitinformationen um
    CObject::CWnd::CProgressCtrl handeln muss.

    Siehe MSDN:

    void f()
    {
       B* pb = new D;                     // unclear but ok
       B* pb2 = new B;
    
       D* pd = dynamic_cast<D*>(pb);      // ok: pb actually points to a D
       ...
       D* pd2 = dynamic_cast<D*>(pb2);   // pb2 points to a B not a D
                                        // cast was bad so pd2 == NULL
       ...
    }
    


  • @HumeSikkins:

    Wo ist denn dann bitteschön der Unterschied zwischen den beiden Zeilen? Wo ist denn dann der Vorteil von static_cast gegenüber dem ()-cast? Mal abgesehen davon, das ersteres C++ ist?

    By the way, ich bin immer bereit Neues zu lernen und mich mit Neuen Dingen auseinanderzusetzen (deswegen schreibe ich hier ja auch immer wieder was). Wenn ich aber keinen wirklichen Nutzen bei Neuen Sachen erkennen kann, dann wende ich die auch selten bis gar nicht an.
    Und ich bestimmt nicht der Einzige, der der Meinung ist, dass wenn ich bis an mein Lebensende weder dynamic_cast noch static_cast verwende, definitiv NICHT auf dem Abstellgleis landen werde.

    Hab mal einen Suchlauf über das VC98-Verzeichnis machen lassen. Kein einziges Mal dynamic_cast gefunden...

    Ich sehe schon die Vorteile von dynamic_cast (unter anderem, dass das ursprüngliche Problem damit NICHT gelöst werden kann ... ;-)), jedoch sehe ich noch keinen Nutzen in meinen Anwendungen. Entweder der Pointer ist auch ein Zeiger auf die Klasse, in die ich Casten will, oder er ist NULL. Und mich durch Klassenhierarchien durchwälzen tue ich auch nicht, dazu gibt's virtuelle Funktionen und ähnliches.

    @ReneG:

    Wenn ich also mit dynamic_cast hier nicht weiterkomme, was macht dann der Ich-arbeite-strikt-nach-C++-Programmierer? Aufgeben? Oder muss er sich mit veralteten Funktionalitäten abfinden?



  • Original erstellt von RenéG:
    **Nochmal zur Frage, warum dynamic_cast nicht klappen kann:

    Ich weiss nicht genau, wo im Speicher, aber eines ist klar:
    Die Laufzeitinformationen, welche bei GetDlgItem zurückgeliefert werden:
    CObject::CWnd

    Ein dynamic_cast auf CProgressCtrl würde voraussetzen, dass es sich bei den Laufzeitinformationen um
    CObject::CWnd::CProgressCtrl handeln muss.

    Siehe MSDN:
    [quote]

    void f()
    {
       B* pb = new D;                     // unclear but ok
       B* pb2 = new B;
    
       D* pd = dynamic_cast<D*>(pb);      // ok: pb actually points to a D
       ...
       D* pd2 = dynamic_cast<D*>(pb2);   // pb2 points to a B not a D
                                        // cast was bad so pd2 == NULL
       ...
    }
    

    **

    [/QUOTE]

    Warum funzt das trotzdem?
    Mit GetDlgItem von CWnd wird dem gelieferten HWND ein schon existierendes CWnd aus der HandleMap zugeordnet, welches direkt auf das Objekt zeigt, als welches es erstellt wurde. Falls diese Zuordnung nicht existiert, wird ein temporäres CWnd-Objekt erzeugt, welchem das HWND zugeordnet wird.

    Erst mal vielen Dank für die Antwort. Am Anfang hattest Du aber geschrieben das es sich um das CWnd handelt das on der Klassenhirachie des MFCControls (ok in diesem Speziellen fall das Prograssdings) handelt. Nun zeigst Du quelltext der Zeigt das es sich nicht um das CWnd handelt sondern um eines das temporär erzeugt wird. (Somit geht der dynamic_cast nicht und es ist das der Fall was weiter oben diskutiert wurde). Das mit der Handlemap hab ich nach suchen ebenfalls gefunden und genau das irritiert mich nun stark da es doch irgendwie niht zusammenpasst 😞

    Wenn wir das eventuell nich klären könnten... Hätt ich es vllt. komplett verstanden und kann es nachvollziehen. Denn das irritiert mich denn nun doch.

    @Shagy

    Die Lösung für das ganze sähe z.B. so aus das Control als variable anzulegen (ctrl + doppelklick auf das MFC Control z.B. ProgressBar) und mit DDX_Control
    arbeiten. (Kann gerne mal ein kleines Beispiel schreiben das zeigt wie ich es bisher machte....)

    @ll auf jedenfall schon mal thx das ihr nicht aufgebt es mir zu erklären 🤡

    [ Dieser Beitrag wurde am 31.03.2003 um 08:59 Uhr von Knuddlbaer editiert. ]



  • @Knuddelbaer
    Das mit dem DDX_Control ist uns allen bekannt.

    Nachteil dieser Methode:
    - +1 Zeile für Membervariable in Headerdatei
    - +1 Zeile in der DoDataExchange-Methode
    - Subclassen des Controls->neue WindowProc, die gar nicht benötigt wird.

    Und das alles nur, weil ich irgendwo eine Methode habe, die den Fortschrittsbalken per SetPos oder StepIt aktivieren soll?

    [ Dieser Beitrag wurde am 31.03.2003 um 09:15 Uhr von RenéG editiert. ]



  • ?? Was passt nicht zusammen ??

    Wurde das Objekt angelegt und initialisiert, so steht der 'echte' Zeiger in der HandleMap der MFC. Dann funzt auch ein dynamic_cast!
    Aber, wenn dieses Objekt existiert, wozu brauche ich dann noch ein GetDlgItem? Dann kann ich ja gleich direkt zugreifen!
    2.
    Gibt es kein passendes Control, wird ein temporäres CWnd angelegt, welches einfach nur das HWND kapselt und ein paar einfachere Funktionen statt API bereitstellt. Der this-Zeiger kann durchaus benutzt werden. Die aufgerufenen Funktionen des Controls, auf das gecastet wird, dürfen allerdings NUR das HWND das CWnd betreffen.


Anmelden zum Antworten