Fortschrittsanzeige



  • Hm, ich glaub so langsam raff ich es *juhu endlich ?! 🤡*

    Also:

    Wenn ich das COntrol mit dem DDX Krempel anlege bekomm ich das CWnd aus der Handlemap.

    In allen anderen Fällen nur ein temporäres CWnd ?

    Die aufgerufenen Funktionen des Controls, auf das gecastet wird, dürfen allerdings NUR das HWND das CWnd betreffen

    Hm, wie kann ich das zuverlässig rausbekommen ?

    thx



    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.

    Und so wie ich das sehe (siehe meine irgendwo weit vorher gezeigten MFC-Source-Auszüge) macht ein CProgressCtrl nix anderes, als Nachrichten an das CWnd (HWND) zu schicken. Also klappt auf jeden Fall ein (CProgressCtrl*)(GetDlgCtrl(ID_MYCTRL)), oder? Aber beim dynamic_cast geht's wieder nicht? Und wie ist es mit static_cast? 😞 😃 😕

    Also, ganz ehrlich, bevor ich mich tagelang mit dynamic_cast rumärgere, mache ich meinen FWuW-Cast! 😉



  • @Knuddlbaer:

    Schau in die MFC-Sourcen (siehe z.B. irgendwo weit oben). Und wenn es ein Standard-WinDoof-Control ist, gibt's grundsätzlich keine eigenen Member-Variablen.



  • In allen anderen Fällen nur ein temporäres CWnd ?

    Nein, erst wird natürlich geschaut, ob es in der permanenten HandleMap schon das Handle gibt!

    Die aufgerufenen Funktionen des Controls, auf das gecastet wird, dürfen allerdings NUR das HWND das CWnd betreffen.

    Hm, vielleicht mit 'sizeof(CProgressCtrl)==sizeof(CWnd)'

    Also, ganz ehrlich, bevor ich mich tagelang mit dynamic_cast rumärgere, mache ich meinen FWuW-Cast!

    Ganz davon abgesehen, dass die Laufzeitinformationen, die ich bei API- und MFC-Programmierung eh nur höchst selten benötige, die Executable unverhältnismässig aufblähen.

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



  • @ReneG:

    Richtig, daran hab ich auch schon gedacht. Was würde ein Sortieralgorithmus für 1.000.000 Elemente mit Verwendung von dynamic_cast wohl länger brauchen?



  • Keine Ahnung, allerdings würde ich für solche Aufgaben eher folgenden Konstrukt verwenden:

    B* b;
    #ifdef _DEBUG
      b = dynamic_cast<B*>( a);
    #else
      b = reinterpret_cast<B*>( a);
    #endif
    

    oder:

    #ifdef _DEBUG
      #define MYCAST( class, ptr) (dynamic_cast<class*>( ptr));
    #else
      #define MYCAST( class, ptr) (reinterpret_cast<class*>( ptr));
    #endif
    

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



  • lol 🙄



  • Hm,

    ich hab mir den Source mal angeschaut, es gibt wirklich ne Handvoll Klassen die keine Member haben.

    Ich darf also bei jedem Zugriff dieser Art prüfen ob es legal ist, auch bei jeder neuen Version der MFC (Microsoft is für alles gut 🤡

    Ich fühle mich dabei dann unsicher vorallem weil ich bei jedem Control was mir über den Weg läuft prüfen darf ob das geht - und das bei jeder neuen Version.

    @Shagy so pauschal wie Du es sagst würde ich sagen das die Kosten des einmaligen Casts bei der Sortierung der 1.000.000 Elemente nicht im verhältnis steht.

    So eine Frage hab ich noch an Rene,

    wann taucht das CWnd denn in der HandleMap nun auf ?!

    thx @ll für die geduld - es hat einiges an Wissen mit sich gebracht 🙂



  • Also gehe ich recht in der Annahme (hoffentlich diesmal 😉 ), dass reinterpret_cast und der ()-cast genau das gleiche machen?

    Und static_cast castet nur zwischen Zeigertypen die in einer Klassenhierarchie untereinanderstehen, aber ohne Runtime-Checking, während dynamic_cast das gleiche macht, nur mit Runtime-Checking?



  • @Knuddlbaer:

    Wenn ich eine Sortierfunktion vom Typ

    int CMyList::Sort(CBase* p1, CBase* p2)
    {
    }

    habe und ich bei jedem Aufruf zweimal dynamic_cast machen muss, dann wird es sicherlich schon einen Unterschied geben. Zumal bei 1.000.000 Elementen diese Funktion wohl etwas häufiger aufgerufen werden wird. Allerdings kann es natürlich sein, dass das checken recht schnell geht. Keine Ahnung, war nur 'ne Idee.

    Bei Standard-WinDoof-Controls gibt es - wie gesagt - grundsätzlich keine eigenen Member-Variablen, bei anderen normalerweise schon. Du musst ja nur in die Header-Datei schauen, ob es Member-Variablen gibt oder nicht. Und das geht schnell.



  • Wenn das Steuerelement von den MFC erstellt wurde, dann befindet sich auch ein Eintrag in der Handle-Map mit einem Zeiger auf das wirkliche Objekt. Dann ist ein Cast doch völlig legal. (static_cast<> )



  • @Knuddelbaer

    wann taucht das CWnd denn in der HandleMap nun auf ?!
    Wenn das Fenster erstellt( Create) oder subclassed (SubclassWindow) wird!
    @Shaggy
    [quote]
    während dynamic_cast das gleiche macht

    Nicht ganz, bei Standardtypen int/char ... funzt dynamic_cast net.



  • wann taucht das CWnd denn in der HandleMap nun auf ?!

    Nach dem es erstellt wurde.



  • @cole
    3x den gleichen Beitrag brauchen wir nicht 😉



  • Doch 🙂



  • wann taucht das CWnd denn in der HandleMap nun auf ?!
    Wenn das Fenster erstellt( Create) oder subclassed (SubclassWindow) wird!

    Ich glaub hier steh ich einfach nur auf'm schlauch und komm allein nich runter.

    Über SubClass weiß ich nix. *gleich mal zugeb - ich kenn mich mit MFC kaum aus, da bin ich noch noob*

    Aber create - wird das nicht generell aufgerufen wenn ein Control erstellt wird ?

    @Shagy

    Wenn ich eine Sortierfunktion vom Typ

    int CMyList::Sort(CBase* p1, CBase* p2)
    {
    }

    Der Sortieralgo arbeitet i.d.r. mit operatoren. Wenn die Klassen vererbt wurden und die RTTI an ist wird auch der passende operator aufgerufen. Wenn die RTTI nicht an ist kann es wieder zu undefinierten bzw. zu falschen sortierergebnissen kommen ?!

    Ich kenne die sortierung der List nicht in der implementierung. Sie könnte z.B. durch den operator < realisiert sein. Und der könnte für CDerived das in der Liste gehalten wird anderst funktionieren als in CBase - eventuell virtual.

    Bei eingeschalteter RTTI kein Problem , ohne RTTI schwer zu durchblicken ?!

    [ Dieser Beitrag wurde am 31.03.2003 um 10:41 Uhr von Knuddlbaer editiert. ]



  • Immer wenn CWnd::Attach aufgerufen wird 🙂



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

    Wenn es dich interessiert, lies doch bitte einfach mal den Artikel. Ich poste doch nicht einen Link aus purer langeweile. Und wenn ich schon einen Link poste, muss ich den Inhalt doch nicht auch noch vorkauen.

    By the way, ich bin immer bereit Neues zu lernen und mich mit Neuen Dingen auseinanderzusetzen

    Tatsächlich. Also den Eindruck machst du *in dieser* Situation nicht auf mich.
    Ich sehe hier eigentlich nur verbohrtheit, was die neuen Casts angehet.

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

    Du weißt aber sicher, dass die meisten Dateien im VC98-Verzeichnis älter als der C++-Standard sind. Und das es damals noch *gar keinen* dynamic_cast gab. Sicher, das weißt du.
    Du weißt bestimmt auch, dass RTTI damals noch gar nicht standardisiert war und das deshlab z.B. die MFC ihre eigene Form des RTTIs mitbringt.

    IEntweder der Pointer ist auch ein Zeiger auf die Klasse, in die ich Casten will, oder er ist NULL.

    Das garantiert dir nur der dynamic_cast.

    Und mich durch Klassenhierarchien durchwälzen tue ich auch nicht, dazu gibt's virtuelle Funktionen und ähnliches.

    Ein großartiges Argument, vorallem wenn es in einem Atemzug mit den C-Casts genannt wird.

    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?

    Sich genau informieren, warum es nicht geht wie es gehen *sollte*. Wenn es überhaupt nicht anders geht, den Vorschlaghammer rausholen. Einen Kommentar dranschreiben und sich bei MS beschweren, dass ihre Lib kaputt ist.

    Wenn es geht, aber vielleicht Geschwindigkeitseinbußen mitsichbringt, *profilen* um zu schauen, ob diese relevant sind. Wenn es nicht anders geht, Vorschlaghammer rausholen und einen Kommentar dranschreiben.

    Aber wie man es auch dreht und wendet. Ich würde sagen, man sollte erst *wissen* was man tut, bevor man den Vorschlaghammer rausholt und undefiniertes Verhalten heraufbeschwört. Einfach mal aus Verdacht alles kurz und kleinzuschlagen ist meiner Meinung nach keine gute Strategie.

    B* b;
    #ifdef _DEBUG
    b = dynamic_cast<B*>( a);
    #else
    b = reinterpret_cast<B*>( a);
    #endif

    Und schon wieder falsch. Bei all dem Sarkasmus und der Ignoranz die ihr hier an den Tag legt, solltet ihr es wenigstens *richtig* machen.
    Ein reinterpret_cast ist *nicht* für downcasts geeignet. Wenn du sicher bist, dass hinter dem a-Zeiger ein B-Objekt steckt heißt die Antwort
    *static_cast*. Die Einschränkungen habe ich schon zwölf-mal geschrieben. Und sie gilt für alle casts außer dem dynamic_cast.
    Der Code sollte also wenn so aussehen:

    B* b;
    #ifdef _DEBUG
      b = dynamic_cast<B*>( a);
    #else
      b = static_cast<B*>( a);
    #endif
    

    Ähnlichen Code wirst du in vielen professionellen Programmen finden. Mal abgesehen davon, dass natürlich vernünftige Coding-Styles Präprozessor-ifs *innerhalb* von Methoden verbieten.

    #ifdef _DEBUG
    #define MYCAST( class, ptr) (dynamic_cast<class*>( ptr));
    #else
    #define MYCAST( class, ptr) (reinterpret_cast<class*>( ptr));
    #endif

    Oder einfach bei der bekannten Syntax bleiben:

    // VC6-Workaround aus Gründen der Übersicht weggelassen.
    #ifdef _DEBUG
    template <class Dest, class Source>
    inline Dest MyCast(Source p)
    {
        return dynamic_cast<Dest>(p);
    }
    #else
    template <class Dest, class Source>
    inline Dest MyCast(Source p)
    {
        return static_cast<Dest>(p);
    }
    #endif
    

    Richtig, daran hab ich auch schon gedacht. Was würde ein Sortieralgorithmus für 1.000.000 Elemente mit Verwendung von dynamic_cast wohl länger brauchen?

    Warum würde ich wohl ein dynamic_cast in einem Sortieralgorithmus anwenden? Da würde ich mir mal eher um mein kaputtes Design Gedanken machen.

    Also gehe ich recht in der Annahme (hoffentlich diesmal ), dass reinterpret_cast und der ()-cast genau das gleiche machen?
    Und static_cast castet nur zwischen Zeigertypen die in einer Klassenhierarchie untereinanderstehen, aber ohne Runtime-Checking, während dynamic_cast das gleiche macht, nur mit Runtime-Checking?

    Nö. Der C-cast ist eine Mischung aus static_cast, reinterpret_cast, const_cast und in C++ eigentlich verbotenen Sachen.

    Der static_cast macht kurz gesagt alle Konvertierungen zwischen S und T möglich, für die es eine umgekehrt implizite Konvertierung von T nach S gibt.
    Dazu gehört unter anderem der downcast.

    [ Dieser Beitrag wurde am 31.03.2003 um 11:20 Uhr von HumeSikkins editiert. ]



  • Das klingt ja alles sehr gut, hab mich gerade kurz mal informatiert und mein "Verdacht" hat sich bestätigt. Ein Informatik-Studi! 😉

    Da ich trotz meiner "Vorschlaghammer"-Programmierung bisher noch nie (und ich programmiere schon sehr lange) undefiniertes Verhalten hatte, wüsste ich nicht, was an meiner "Vorschlaghammer"-Programmierung grundlegend falsch ist. Oder warum was anderes verwenden sollte, wenn es auch weiterhin funktioniert.

    Und zu meinem Satz

    Entweder der Pointer ist auch ein Zeiger auf die Klasse, in die ich Casten will, oder er ist NULL.

    der mit

    Das garantiert dir nur der dynamic_cast.

    beantwortet wurde, muss ich mich vielleicht deutlicher Ausdrücken:

    Wenn ich eine Funktion habe, wie z.B.

    void CMyWnd::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
    {
    }
    

    und ich weiß, dass lParam ein NHMDR* ist, dann kann ich mir hundertprozentig sicher sein, dass

    NMHDR* pInf = (NMHDR*)lParam;
    

    nicht zu undefiniertem Verhalten führt! Und wenn ein lParam NULL ist, dann ist auch pInf gleich NULL. Dann füge ich noch ein ASSERT(pInf != NULL) hinzu, und schon weiß ich, dass alles Ok ist. Hab noch nie das Problem mit falschen Zeigern gehabt. Entweder sie waren Ok oder NULL. Und wenn die nicht sein durften, dann hat mir das das ASSERT gesagt und der Fehler wurde bereinigt, nach mehrfachem Testen wurde die Release kompiliert. Irgendwann trat dann doch mal ein Fehler auf, dann hab ich die Debug-Version genommen, den Fehler nachvollzogen und das Problem beseitigt. Und alles ohne dynamic_cast, static_cast, reinterpret_cast und was es sonst noch so alles gibt.



  • Das klingt ja alles sehr gut, hab mich gerade kurz mal informatiert und mein "Verdacht" hat sich bestätigt. Ein Informatik-Studi!

    Ja und? Was willst du damit sagen? Ich habe weder C++ entwickelt noch den C++ Standard geschrieben.
    Ich kann dir aber in diesem Fall sagen, wie es ist und was im Standard steht. Wenn du ein Problem damit hast, dir etwas von einem "Informatik-Studi" sagen zu lassen, dann sag das. In diesem Fall werde ich selbstverständlich nicht mehr auf deine Postings eingehen.

    Da ich trotz meiner "Vorschlaghammer"-Programmierung bisher noch nie (und ich programmiere schon sehr lange) undefiniertes Verhalten hatte

    Wie willst du das beurteilen, wenn du an den Regeln die der C++ Standard aufstellt nicht interessiert bist? Schau dir doch den Verlauf des Threads einfach mal an. Ich behaupte mal, dass es keinen C++ Programmierer gibt, der nicht schonmal über undefiniertes Verhalten gestolpert ist. Ob er dies bemerkt hat steht auf einem ganz anderen Blatt.

    Wenn ich eine Funktion habe, wie z.B.

    void CMyWnd::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
    {
    }
    und ich weiß, dass lParam ein NHMDR* ist, dann kann ich mir hundertprozentig sicher sein, dass

    NMHDR* pInf = (NMHDR*)lParam;
    nicht zu undefiniertem Verhalten führt!

    Richtig. Ich rede ja auch von Situationen wo du *nicht* hunderprozentig weißt, von welchem Typ das Objekt ist, auf das ein Basisklassenzeiger verweist. Es geht hier um Laufzeitpolymorphie und nicht um irgendwelche Krücken namens LPARAM. Hier könntest du überhaupt keinen dynamic_cast einsetzen, selbst wenn du es wolltest.

    In Situationen wo du *vorher genau* weißt und *wissen musst*, von welchem Typ ein Objekt ist, du aber nur eine Basisklassenreferenz hast, stellt sich die Frage, warum du dieses Wissen überhaupt aufgibst. Wenn du auf den Cast angewiesen bist, verhält sich der Code nicht polymorph. In diesem Fall stellt sich die Frage, warum er dann so aufgebaut ist. Da gibt es zwei Möglichkeiten: Entweder man verwendet gleich den passenden Typ oder man packt die Funktionalität in virtuelle Methoden und kann damit auf den downcast verzichten.


Anmelden zum Antworten