Zugriff auf Klasse über NULL Pointer



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



  • Wenn ich einen Pointer habe, dann arbeite ich auch weiterhin mit einem Pointer:

    if (A* p = dynamic_cast<A*>(pb))
    	p->foo();
    

    Deshalb kann man allgemein nicht sagen, das dies oder das besser sei.



  • Shlo schrieb:

    Deshalb kann man allgemein nicht sagen, das dies oder das besser sei.

    Exakt, die Semantik ist naemlich total verschieden.

    Den Referenzen-Cast nimmt man dann, wenn man sich sicher ist, dass der Cast erfolgreich sein wird und ein Fehlschlagen eine Ausnahme ist, die theoretisch nicht vorkommen sollte (wobei bei sowas ein checked_cast nicht besser waere). Wenn man den Pointer-Cast macht, dann prueft man meisstens ob es wirklich der richtige Typ ist - man geht also davon aus, dass der Cast durchaus fehlschlagen kann und reagiert dementsprechend darauf.



  • wo liegt eigentlich der sinn im reference cast?
    ist damit irgendwas möglich, was sonst nich möglich wär?



  • otze schrieb:

    wo liegt eigentlich der sinn im reference cast?

    Was spricht gegen ihn?
    Du verwendest ihn, statt

    if(!(p=dynamic_cast<Derived*>(base)))
      throw Error();
    

    das ist doch durchaus sinnvoll



  • kann man mit dem reference cast von ner baseklass zu ner referenz auf nen derived casten? 😮 ok, daran hab ich net gedacht^^



  • otze schrieb:

    kann man mit dem reference cast von ner baseklass zu ner referenz auf nen derived casten? 😮 ok, daran hab ich net gedacht^^

    Genau für diese Möglichkeit hat man den dynamic_cast doch eingebaut.

    Edit:

    Vergesst mein Posting, habe vergessen, dass das nen alter Thread ist 🙄


Anmelden zum Antworten