Programm stürzt ab beim Zuweisen einer Variable


  • Mod

    DerCoder schrieb:

    und außerdem die Klassen auf meinen FTP-Server geladen.

    Ahh, das ist gut, ich hatte deinen edit weiter oben nicht gesehen.



  • Selbst wenn ich mich mit einem Debugger auskennen würde, wäre mir damit nicht viel geholfen. Warum sollte denn das Zuweisen einer Variable in der Basisklasse funktionieren und das Zuweisen einer Variable des gleichen Types in einer Unterklasse mit einer Access Violation enden?


  • Mod

    DerCoder schrieb:

    Selbst wenn ich mich mit einem Debugger auskennen würde, wäre mir damit nicht viel geholfen.

    Der Debugger gilt als wichtigstes Werkzeug des Programmierers. Jetzt wäre doch eine gute Gelegenheit, sich damit mal näher zu beschäftigen 🙂

    Zuweisen einer Variable des gleichen Types in einer Unterklasse mit einer Access Violation enden?

    Weil du irgendwo undefiniertes Verhalten erzeugst. Wie schon gesagt sind die vielen reinterpret_casts in deinem Programm ein fast sicheres Zeichen, dass da irgendetwas faul ist. Compiler haben nämlich meistens Recht. Besonders wenn du selber noch unerfahren bist, solltest du eher dem Compiler vertrauen, als dir selber.



  • DerCoder schrieb:

    Selbst wenn ich mich mit einem Debugger auskennen würde, wäre mir damit nicht viel geholfen. Warum sollte denn das Zuweisen einer Variable in der Basisklasse funktionieren und das Zuweisen einer Variable des gleichen Types in einer Unterklasse mit einer Access Violation enden?

    Weil das von getChild gelieferte UIElement gar kein UIButton ist? Ich würde mal ein dynamic_cast probieren. Wenn der 0 liefert, war es kein UIButton.

    Im Debugger könnte man das vermutlich auch direkt sehen.



  • Nein, manni66, das passt alles. Ich kann ja auch Memberfunktionen vom UIButton aufrufen, ohne dass es Probleme gibt. Nur das Zuweisen geht nicht.

    Verschiebe ich allerdings die Zeile

    std::function<void (UIButton*> onClick;
    

    aus der UIButton.hpp in die UIElement.hpp, klappt es. Das Programm stürzt nicht ab. Anscheinend habe ich keinen Zugriff auf die über die Zuweisung übergebene Funktion in meiner Unterklasse.

    Aber wie gesagt, ich kann dazu nichts sagen, weil ich mir das absolut nicht erklären kann.



  • Moment, ich glaube, du hast die Fehlermeldung falsch verstanden.
    Das Access Violation bezieht sich nicht auf Konstrukte, die C++ bereitstellt, wie die Schutzstufen protected und private bei Klassen, sondern auf Zugriffe auf den Arbeitsspeicher. Das Auftreten der Meldung heißt, dass du auf Speicher zugegriffen hast, auf den du nicht zugreifen durftest. Beispiel:

    #include <iostream>
    #include <ostream>
    
    int main()
    {
      int* Zeiger_Irgendwohin = reinterpret_cast<int*>(0xBADFA15E);
        //Hier passiert noch nichts, aber das Übel ist nah
        //Vielleicht steht hier auch noch ein Haufen Zeug
      int WirdNix = *Zeiger_Irgendwohin;
        //Hier wird dann eine Access Violation, eine Speicherzugriffsverletzung,
        //vom BS ausgelöst (bei allen modernen, Nischensysteme ausgenommen ;) )
      std::cout << WirdNix << std::endl;
        //Ohne die Ausgabe wurde der Zugriff beim Test auf Codepad rausoptimiert
    }
    

    Dem C++-Compiler ist es erstmal völlig egal, dass da dem Zeiger einfach irgendeine erfundene Konstante zugewiesen wird (der Typ wird ja explizit geändert), schließlich kompillierst du vielleicht für irgendeine Embedded-Architektur, wo du sicher an diese Stelle schreiben kannst.



  • DerCoder schrieb:

    Nein, manni66, das passt alles. Ich kann ja auch Memberfunktionen vom UIButton aufrufen, ohne dass es Probleme gibt. Nur das Zuweisen geht nicht.

    Ja und?



  • manni66 schrieb:

    DerCoder schrieb:

    Nein, manni66, das passt alles. Ich kann ja auch Memberfunktionen vom UIButton aufrufen, ohne dass es Probleme gibt. Nur das Zuweisen geht nicht.

    Ja und?

    Ja, das heißt doch, dass mein Zeiger geht 😃 Außerdem muss ich static_cast<> nehmen und nicht dynamic_cast<>, weil ich ja nur die Zeiger auf die Klassenobjekte ableite.

    Fast2_unreg schrieb:

    Moment, ich glaube, du hast die Fehlermeldung falsch verstanden.

    Nein, das glaube ich nicht. Würde ich auf eine als private deklarierte Variable zugreifen wollen, schreitet der Compiler schon zur Kompilierungszeit ein.

    Folgendes hat sich herauskristallisiert:

    // UIElement.hpp
    
    class UIButton;
    typedef std::function<void (UIButton*)> OnClickCallback;
    
    class UIElement {
        private:
        public:
            OnClickCallback onClick;
    }
    
    ...
    // main.cpp
    window->getChild(IDC_SAVE)->onClick = MeineCallbackFunktion; // FUNKTIONIERT! Keine Access Violation
    
    // UIButton.hpp
    
    typedef std::function<void (UIButton*)> OnClickCallback;
    
    class UIButton : public UIElement {
        private:
        public:
            OnClickCallback onClick;
    }
    
    ...
    // main.cpp
    ((UIButton*) window->getChild(IDC_SAVE))->onClick = MeineCallbackFunktion; // FUNKTIONIERT NICHT! 0xc000000005
    

  • Mod

    Da ist ja immer noch reinterpret_cast drin, wundert dich da ein Absturz? Wie erzeugst du denn das "child"? Vermutlich ist das gar kein UIButton.



  • DerCoder schrieb:

    manni66 schrieb:

    DerCoder schrieb:

    Nein, manni66, das passt alles. Ich kann ja auch Memberfunktionen vom UIButton aufrufen, ohne dass es Probleme gibt. Nur das Zuweisen geht nicht.

    Ja und?

    Ja, das heißt doch, dass mein Zeiger geht

    Nein

    DerCoder schrieb:

    Außerdem muss ich static_cast<> nehmen und nicht dynamic_cast<>, weil ich ja nur die Zeiger auf die Klassenobjekte ableite.

    Aha, wie leitet man denn Zeiger ab?



  • Welches reinterpret_cast<> meinst du?

    Die childs werden als UIElement erzeugt undzwar wie folgt:

    // im DialogProc:
    case WM_INITDIALOG: {
    	EnumChildWindows(_hWnd, EnumChildProc, reinterpret_cast<LPARAM> (this));
    } break;
    
    BOOL CALLBACK UIWindow::EnumChildProc(HWND hWnd, LPARAM lParam) {
    	UIWindow* window = reinterpret_cast<UIWindow*> (lParam);
    	UIElement* child;
    
    	child = new UIElement(hWnd, GetDlgCtrlID(hWnd), window);
    	window->_children += child;
    
    	return true;
    }
    

    Also Fakt ist, sie werden nicht als UIButton o.Ä. erzeugt, aber der Cast zu einem UIButton funktioniert.



  • DerCoder schrieb:

    Also Fakt ist, sie werden nicht als UIButton o.Ä. erzeugt, aber der Cast zu einem UIButton funktioniert.

    Du bist ein Held ...


  • Mod

    Damit wäre alles geklärt. Casts sind keine Magie. (Reinterpret-)Casts sagen dem Compiler nur: "Behandle dieses Objekt, als ob es in Wirklichkeit XYZ wäre, obwohl es für dich gerade wie ein ABC aussieht". Da wird nichts wirklich umgewandelt. Diese Casts sind die Methode des Programmierers zu sagen, dass er es ausnahmsweise besser weiß als der Compiler, weil der Programmierer irgendwelche Zusatzinformationen hat. Hier ist es wohl eher so, dass du keine Ahnung hast, was du da tust und den Compiler zum Schweigen bringst.

    Schritt für Schritt passiert folgendes:

    child = new UIElement(hWnd, GetDlgCtrlID(hWnd), window);
    

    Ein UIElement-Objekt wird erzeugt.

    ...
    window->getChild(IDC_SAVE)
    

    Du holst einen Zeiger auf dieses UIElement...

    ((UIButton*) window->getChild(IDC_SAVE))
    

    Hier sagst du dem Compiler, dass dieser Zeiger eigentlich auf einen UIBUtton zeigt, obwohl er wie ein Zeiger auf ein UIElement aussieht. Daher akzeptiert der Compiler den darauf folgenden Zugriff auf ein UIButton-Member:

    ((UIButton*) window->getChild(IDC_SAVE))->onClick
    

    Aber das onCLick ist gar nicht da, da getChild auf ein UIElement zeigt, das kein onClick hat. BUMM!

    Welches reinterpret_cast<> meinst du?

    Casts im C-Stil (also die mit den Klammern: (Typ)Variable , wie dein (UIButton*) window->getChild(IDC_SAVE) sind trickreich. Sie können je nach Kontext ein static_cast, ein const_cast oder ein reinterpret_cast oder gar eine Kombination aus mehrerem sein. Wenn man sich nicht verdammt gut auskennt, weiß man nie, was man bekommt. Und selbst wenn, dann kann eine kleine Änderung ganz woanders die Bedeutung total verändern. Also Finger weg! Außerdem sind sie nicht gut sichtbar und nicht durch einfache Textsuche im Quelltext findbar.

    Im Rest deines Quellcodes wimmelt es auch noch von mehr reinterpret_casts. Wie schon gesagt geht dadurch die Verantwortung voll auf den Programmierer über, dass alles seine Richtigkeit hat. Nichts für ungut, aber deine Sprachkenntnisse sind einfach noch nicht so weit, dass du solche Sachen tatsächlich besser weißt als der Compiler. Man kann und sollte lieber sauber programmieren, so dass man überhaupt nicht casten braucht. Da lernt man viel mehr und C++ spielt seine Stärke so richtig aus: Durch die strenge Typprüfung werden so nämlich sehr viele Logikfehler zu Typfehlern, so dass diese schon zur Compilezeit mit aussagekräfitger Fehlermeldung quittiert werden, anstatt dass man umständlich Laufzeitfehler (Abstürze) beheben muss.



  • Achso das hatte ich gar nicht bedacht 😞

    Kann ich denn irgendwie aus meinem UIElement ein UIButton machen? Also nachträglich? Sonst müsste ich nämlich beim Erstellen meines UIElement-Objekts prüfen, ob die Klasse des Childwindows (dess Button) == "Button" ist. Und das macht alles ziemlich langsam, oder?


  • Mod

    DerCoder schrieb:

    Sonst müsste ich nämlich beim Erstellen meines UIElement-Objekts prüfen, ob die Klasse des Childwindows (dess Button) == "Button" ist.

    Wieso?

    Mal ehrlich: Das Design scheint ziemlicher Murks zu sein, wenn obige Frage aufkommt. Du solltest erst einmal gründlich Erfahrung sowohl mit C++ als auch mit den bekannten Frameworks sammeln. Dann verstehst du auch, was die machen, wie die das machen und warum die das so machen. Wenn du dann immer noch den Wunsch verspürst, ein eigenes Framework zu schreiben (warum eigentlich? Was ist hier das Ziel?), dann kannst du dich viel besser auf dein eigentliches Ziel konzentrieren, anstatt mit den Grundlagen zu kämpfen.



  • Ohman, das deprimiert mich jetzt 😢 Ich programmiere schon seit 2 Jahren :c
    Ich möchte gern mein eigenes Framework benutzen, weil ich sonst den Überblick über die verfügbaren Funktionen verliere. Außerdem finde ich diese relativ kompakte Schreibweise in meiner main-Funktion besser, als wenn sie über 50 Zeilen verfügt. Und bei meinen kleinen Projekten habe ich gerne meine eigenen Klassen und Libs. Und außerdem lerne ich dadurch ja auch was ^^ Ich wär niemals auf das Problem mit den Casts gestoßen, hätte ich nicht diese Klassen geschrieben, oder?

    Hast du ne Idee, wie ich das auf meine Art hinkrieg?


  • Mod

    DerCoder schrieb:

    Hast du ne Idee, wie ich das auf meine Art hinkrieg?

    Dafür müsste ich mich erst einmal grundlegend in dein Framework hinein denken. Und wahrscheinlich würde ich dann sowieso alles ändern, was dann im Aufwand gleich wäre, mein eigenes Framework zu schreiben. Also eher nein 😞 .

    Fakt ist jedenfalls, wenn du einen UIButton willst, musst du einen UIButton erzeugen kein UIElement. Herbeizaubern geht nicht.



  • DerCoder schrieb:

    Ich wär niemals auf das Problem mit den Casts gestoßen, hätte ich nicht diese Klassen geschrieben, oder?

    Nachdem du dich mit Klassen/Objekten, Vererbung, Zeigern & Casts nicht wirklich auskennst, hättest du fürher oder später garantiert an anderer Stelle ein Problem mit Casts bekommen.

    Hast du ne Idee, wie ich das auf meine Art hinkrieg?

    Ganz einfach: du musst das erzeugen was du haben willst.
    Und nein, dadurch wird nicht alles furchtbar langsam. Computer sind ziemlich schnell.



  • hustbaer schrieb:

    Und nein, dadurch wird nicht alles furchtbar langsam.

    Ich meinte auch eher das auslesen der Klasse aus meinem HWND und das anschließende Vergleichen des Klassennamen mit "Button".



  • Vielleicht, vielleicht auch nicht. Vielleicht gibt es auch bessere Möglichkeiten, aber erstmal solltest du das Programm so umschreiben, dass es läuft und macht was es tut. Und zwar ohne casts. Wenn du dann Geschwindigkeitsprobleme haben solltest, kannst du das optimieren. Aber nur mal so: normalerweise wird es nicht langsamer, wenn du MEHR Typinformationen hinzufügst (UIElement? was ist das? Ohh, es ist vielleicht ein Button? dann muss ich prüfen ob es einer ist. vs: "hey, ein Button")

    Und ansonsten:

    First make it run, than make it fast.


Anmelden zum Antworten