Zugriff auf Klasse über NULL Pointer



  • volkard schrieb:

    ich denke auch, das wird noch ne weile so bleiben.

    Wahrscheinlich. Solange in den MFC noch abenteuerliche Konstrukte wie

    HWND CWnd::GetSafeHwnd()
    {
        if(this == NULL)
            return NULL;
        else
            return m_hWnd;
    }
    

    zu finden sind, wird VC++ das wohl auch weiterhin unterstützen (müssen)... 🙄



  • tag schrieb:

    if(this == NULL)
            return NULL;
        else
            return m_hWnd;
    

    rofl!



  • volkard schrieb:

    tag schrieb:

    if(this == NULL)
            return NULL;
        else
            return m_hWnd;
    

    rofl!

    looooooool



  • Ich finde es eher zum heulen



  • Wie soll das denn funktionieren? Eine Klasse, für die kein Speicher existiert, soll in einer Memberfkt. Null zurückgeben? Kann ich mir nicht so ganz vorstellen 😞



  • ethereal schrieb:

    Wie soll das denn funktionieren? Eine Klasse, für die kein Speicher existiert, soll in einer Memberfkt. Null zurückgeben? Kann ich mir nicht so ganz vorstellen 😞

    Das geht schon, dass Problem ist, dass in der Funktion auf einen Member zurückgegrifen wird, dass gibt Probleme.

    Generell geht das, da die Compiler idr. folgenden C++ Code

    class foo {
      int i;
    public:
      foo(int j) : i(j) { }
      void add_10() { i+=10; }
      int do_something() { return 100*std::time(NULL); }
    };
    
    int main() {
      foo bar(10);
      bar.add_10;
      bar.moep();
    }
    

    in ungefähr folgendes C Code äquivalent umsetzen

    struct foo {
      int i;
    };
    
    void foo_constructor(int i,struct foo *this;) { this->i=1; }
    void foo_add_10(struct foo *this) { this->i+=10; }
    void foo_moep(struct foo *this) { return 100*time(); }
    
    int main() {
      struct foo bar;
      foo_constructor(10,&bar);
      foo_add_10(&bar);
      foo_moep(&bar);
    }
    

    und bei foo_moep ist es ja egal, wohin this zeigt, da es ja nicht deferenziert wird.



  • kingruedi schrieb:

    und bei foo_moep ist es ja egal, wohin this zeigt, da es ja nicht deferenziert wird.

    In der Praxis wird der Zeiger zwar nicht dereferenziert,
    der C++ Standard ist in diesem Punkt aber eindeutig. Dort steht, dass foo->bar() dazu führt, dass foo dereferenziert wird. Damit hat der Aufruf einer Memberfunktion über einen Nullzeiger *immer* undefiniertes Verhalten.

    Code wie:

    ((Foo*)0)->func();
    

    ist schlicht und einfach kein gültiges C++.



  • @Hume
    ja natürlich ist das höchst illegal und es macht ja auch keinen Sinn, weil der C++ Standard für so etwas ja extra static-Member eingeführt hat.



  • Dass es Sinn machen kann, zeigt der MS-Code doch. Es ist ja auch größtenteils portabel. Nur halt theoretisch illegal und absolut grottiger Stil, aber IMHO nicht die Aufregung wert, die hier darum gemacht wird 🙄



  • operator void schrieb:

    Dass es Sinn machen kann, zeigt der MS-Code doch.

    Echt? Das sehe ich ehrlich gesagt anders. Ich kann keinen Sinn darin erkennen eine Memberfunktion auf einem Nullpointer aufzurufen, sprich eine Nachricht an ein Objekt zu senden, wo ein Nullpointer ja garantiert *kein* Objekt ist. Da ist imo kein Sinn sondern lediglich eine Brechstange mit der OOP passend geprügelt wird. Oder anders: vielleicht macht es aus technischer Sicht, im Modell (oder der Abtstraktion) aber doch nicht.

    Null Objekte auf der anderen Seite sind durchaus sinnvoll, aber das sind ja auch vollkomen andere Biester.

    Es ist ja auch größtenteils portabel.

    Aus semantischer Sicht imo trotzdem völliger quatsch.

    aber IMHO nicht die Aufregung wert, die hier darum gemacht wird

    Welche Aufregung genau meinst du?



  • @Hume
    Aus einem präexception Standpunkt macht es Sinn.

    Oft wird behauptet, dass man den return value von jeder WinAPI/MFC Funktion auf Fellschlag testen muss, das ist aber nicht ganz richtig. Jede WinAPI/MFC Funktion testet zuerst ob die Parameter gültig sind, das heist im Falle von Pointer, dass sie nicht 0 sind. Wenn sie ungültig sind dann gibt die Funktion einen Fehlerwert zurück, welcher dann an die nächste Funktion weiter gegeben wird welche dann wieder einen Fehlerwert zurück gibt. Also:

    HWND hwnd=CreateWindow(...);
    void*ptr=malloc(24);
    DoWindowStuff(GetWindowInfoStuff(hwnd),...);
    DoOtherStuff(hwnd,...);
    free(ptr);
    CloseWindow(hwnd);
    if(!hwnd)
      MessageBox(0,"CreateWindow failed","Error",16);
    

    Das hier wird also sicher (sollte zumindest) nicht abschmieren, desweiteren ist die Fehlerbehandelung vom Auftritt getrennt und wichtige Funktionen wie free werden aufgerufen. Also eine Art C Exception.

    Heute würde man das mit Exceptions und Destruktoren lösen aber da WinAPI C ist und Exceptions damals noch lange nicht von jedem Compiler unterstützt wurden wurde das so gemacht.

    Das Beispiel ist zwar MFC aber da das nur ein WinAPI Wrapper ist es irgendwie verständlich, dass es Fehler auf die gleiche Art behandelt (verbessert mich wenn ich falsch liege aber MFC benutzt ja keine C++ Exceptions).



  • HumeSikkins schrieb:

    Echt? Das sehe ich ehrlich gesagt anders. Ich kann keinen Sinn darin erkennen eine Memberfunktion auf einem Nullpointer aufzurufen, sprich eine Nachricht an ein Objekt zu senden, wo ein Nullpointer ja garantiert *kein* Objekt ist. Da ist imo kein Sinn sondern lediglich eine Brechstange mit der OOP passend geprügelt wird.

    Vielleicht kommt da teilweise meine Delphi-Vergangenheit durch (TObject.Free tut bei einem Nullzeiger auch nichts). Ich fands einen C+-Hack wie jeden anderen auch. Eigentlich ist das Präfix "Safe" schon ein ganz böses Omen... 🙂

    Es ist ja auch größtenteils portabel.

    Aus semantischer Sicht imo trotzdem völliger quatsch.

    Wollte ich nicht bestreiten. Aber wenns tatsächlich mal geholfen hat, den Code übersichtlich zu halten, ist es immerhin noch ein relativ portabler Hack. Da gibts gefährlichere Missbräuche von UB, nur dass die eben nicht so offensichtlich und dreist sind. Ich dachte, das lol und rofl hätten auch darauf abgezielt. Wenns nur um die Semantik ging, nehme ich mein Posting zurück 🙂



  • hi leute, sorry dass ich den thread nochmal raufhole aber ich hab hier was interessantes 😉

    Ich hab einfach mal meinem Freund 😉 Scott Meyers (Kolumnist im C++ Report und Autor der "Effective" Reihe) geschrieben, als Antwort bekam ich prompt (SOGAR IN DEUTSCH!!!):

    Danke. Es freut mich, daß Sie meine Bücher nützlich gefunden haben.

    I've discovered some strange behavior with null pointer to classes - but I
    could imagine you already know that 🙂
    So, if I call a normal member function of a class via a null-pointer to the class, the function is handled like a static function of the class.
    Is this behavior undefined, not portable, compiler dependent?

    Es ist in prinzip nicht möglich, ein null this-pointer zu haben, weil es nicht möglich sein sollte, ein Mitgliederfunktion ohne ein Objekt aufzurufen. Deswegen ist solche Code bestimmt nicht portable.

    I don't think that it is defined in the c++ standard, but why does MS Visual C++ and g++ support it? Even in the MFC there are constructs like:
    if (this == NULL)
    > return NULL;
    else
    > return m_hWnd;

    Ich habe keine Ahnung, warum solche Code in MFC besteht, aber mir is MFC ein Geheimnis 🙂

    Scott

    Also hier haben wirs nochmal von einem Profi erklärt. 😃

    greetz



  • operator void schrieb:

    Dass es Sinn machen kann, zeigt der MS-Code doch.

    Ich habe ja einen bösen Verdacht wofür die Konstruktion eingeführt wurde

    dynamic_cast<child*>(pointertobase)->DoDirtyStuff()
    

    Wenn so bei Winzigweich Fehler abgefangen werden dürfte so einiges klar werden 😃



  • joX schrieb:

    operator void schrieb:

    Dass es Sinn machen kann, zeigt der MS-Code doch.

    Ich habe ja einen bösen Verdacht wofür die Konstruktion eingeführt wurde

    dynamic_cast<child*>(pointertobase)->DoDirtyStuff()
    

    Wenn so bei Winzigweich Fehler abgefangen werden dürfte so einiges klar werden 😃

    Also da bin ich dann aber doch für:

    try{
      dynamic_cast<child&>(*pointertobase).DoDirtyStuff()
    }catch(bad_cast&){
      ...
    }
    

    Denn wie stelt man beim ersten fest ob es nicht geklappt hat oder nicht?



  • Man sollte prüfen ob ein null-Pointer zurückgegeben wurde.



  • Irgendwer schrieb:

    Also da bin ich dann aber doch für:

    try{
      dynamic_cast<child&>(*pointertobase).DoDirtyStuff()
    }catch(bad_cast&){
      ...
    }
    

    Denn wie stelt man beim ersten fest ob es nicht geklappt hat oder nicht?

    Shlo schrieb:

    Man sollte prüfen ob ein null-Pointer zurückgegeben wurde.

    Ich dachte das standard Verhalten von dynamic_cast sein einen NUll-Pointer zurueck zu geben und nicht eine Exception zu schmeißen. Und die herzallerliebeste MS-Implementation

    HWND CWnd::GetSafeHwnd()
    {
        if(this == NULL)
            return NULL;
        else
            return m_hWnd;
    }
    

    fragt den Null-Pointer (zwar nicht ISO konform) ja schon irgendwie ab :D.



  • Ok, die Lösung ist zwar nicht portabel, aber das war auch nie Anforderung der MFC. Die wird ausschließlich mit dem MS Compiler verwendet, und das war's. Gut, Borland und Watcom (und vielleicht ein paar andere) haben sie damals auch in angepassten Versionen gebundled, aber das ist ihr Problem.

    Ich glaub dynamic_cast wird nicht viel verwendet in der MFC, weil's das zu der Zeit noch gar nicht gegeben hat. Aber so wie man für "kein HWND" einfach 0 übergibt, so macht man's jetzt mit "kein CWnd" und NULL. Praktisch ist die GetSafeHWND Schreibweise doch. Wie sie implementiert ist, ist Sache des Compilerherstellers und sollte nicht dein Problem sein.

    In jeder x-beliebigen Runtime Library jedes x-beliebigen C/C++ Compilers stecken haufenweise ähnliche oder noch viel schlimmere Schweinereien, aber solang's funktioniert, ist ja nieman damit geschadet.

    Oder schau dir mal C++Builder/VCL an. Das geht's wirklich wüst zu, nicht so lächerliche Kleinigkeiten wie this==0!



  • joeX schrieb:

    Ich dachte das standard Verhalten von dynamic_cast sein einen NUll-Pointer zurueck zu geben und nicht eine Exception zu schmeißen.

    Nicht ganz, bei einem Pointer-Cast wird ein null-Zeiger zurückgegeben, bei einem Referenz-Cast dagen wird std::bad_cast geworfen, da eine Referenz niemals null sein kann.



  • Und deshalb ist:

    dynamic_cast<child&>(*ptr).foo();
    

    besser als:

    if(dynamic_casr<child*>(ptr)->foo())
      static_cast<child*>(ptr)->foo();
    else
      //error...
    

    Oder jedliche compiler spezifischen Wrapper.


Anmelden zum Antworten