Zeiger = 0;



  • class Data;
    
    struct Pointer
    {
        Data* data;
    };
    
    int main(void)
    {
        Pointer* P1 = new Pointer;
        P1->data = new Data(//blablaa);
        P1 = 0;
    
        return 0;
    }
    

    Wenn jetzt der Zeiger 'P1' in Nichts zeigt, was passiert mit dem Inhalt, auf den er gezeigt hatte?



  • Der ist noch immer dort wo er vorher war. Nur hast du leider vergessen wo. Er ist also für immer verloren.



  • Das Objekt bleibt an der Stelle und der Speicher bleibt reserviert. Und freigeben kannst du ihn dann auch nicht mehr.



  • Shade Of Mine schrieb:

    Der ist noch immer dort wo er vorher war. Nur hast du leider vergessen wo. Er ist also für immer verloren.

    Ein Speicherleck entsteht?



  • RussianTux schrieb:

    Shade Of Mine schrieb:

    Der ist noch immer dort wo er vorher war. Nur hast du leider vergessen wo. Er ist also für immer verloren.

    Ein Speicherleck entsteht?

    Exakt.
    Alles was du mit new anlegst musst du ja mit delete löschen. Und wenn du vergisst wo das Ding ist, dass du angelegt hast - kannst du ja nicht mehr mit delete löschen. Ergo: ja, Speicherleck.



  • Shade Of Mine schrieb:

    RussianTux schrieb:

    Shade Of Mine schrieb:

    Der ist noch immer dort wo er vorher war. Nur hast du leider vergessen wo. Er ist also für immer verloren.

    Ein Speicherleck entsteht?

    Exakt.
    Alles was du mit new anlegst musst du ja mit delete löschen. Und wenn du vergisst wo das Ding ist, dass du angelegt hast - kannst du ja nicht mehr mit delete löschen. Ergo: ja, Speicherleck.

    class Data; 
    
    struct Pointer 
    { 
        Data* data; 
    }; 
    
    int main(void) 
    { 
        Pointer* P1 = new Pointer; 
        P1->data = new Data(//blablaa);
    
        delete P1->data;
        delete P1;
    
        return 0; 
    }
    

    Wäre dies der richtige Weg um 'data' und 'P1' loszuwerden oder könnte man "delete P1->data;" überspringen und direkt bei "delete P1;" anfangen?



  • Ist ein möglicher Weg so wie du es dastehen hast.
    Wenn du P1 zuerst löschst und dann nochmal darauf zugreifst, sei es nur um data zu löschen, dann erzeugst du undefiniertes Verhalten.
    Es kann gut gehen. Das Programm kann aber auch abstürtzen. Es können in China auch in dem Moment dann 20 Reissäcke umfallen. Der Standard garantiert dir für nichts.

    Alternativ könntest du natürlich einen Destruktor in deiner Struktur definieren, der dann data platt macht, denn dann reicht der aufruf von delte P1;


  • Mod

    RussianTux schrieb:

    Wäre dies der richtige Weg um 'data' und 'P1' loszuwerden oder könnte man "delete P1->data;" überspringen und direkt bei "delete P1;" anfangen?

    Nein. Der als "richtig" geltende Weg wäre, dass ein Objekt sich selber um seine Ressourcen kümmert (oder besser noch sie an Ressourcenhandlerklassen auslagert) und sie auch insbesondere selber wieder freigibt:
    Google: RAII

    int main(void)
    {
    Pointer* P1 = new Pointer;

    //...

    delete P1;
    }

    Dies ist totaler Mist. Wenn ein new und ein dazugehöriges delete im gleichen Scope stehen, dann ist dies das gleiche wie ein Objekt mit automatischer Speicherklasse, bloß in schlecht (sowohl Performance als auch Probleme beim unerwartetem Verlassen des Scopes). Kommst du von Java?



  • Vielleicht will er auch nur den Umgang mit Zeigern üben 😉



  • hustbaer schrieb:

    Vielleicht will er auch nur den Umgang mit Zeigern üben 😉

    Und das geht nur, indem man fehlerhaften Mist produziert?



  • hustbaer schrieb:

    Vielleicht will er auch nur den Umgang mit Zeigern üben 😉

    So ist es, habe das Thema Zeiger nochmals üben müssen weil ich da noch viele Lücken in der Theorie freigelassen habe.

    class Data;
    
    class Pointer
    {
    private:
        Data* data;
    public:
    
        Pointer(Data new_data)
        {
            data = new Data(new_data);
        }
    
        ~Pointer()
        {
            if(data != 0)
                delete data;
        }
    };
    
    int main(void)
    {
        Data d;
        Pointer* P1 = new Pointer(d);
    
        // "delete P1;" weggelassen da nicht erforderlich
    
        return 0;
        // objekt wird beim beenden automatisch gelöscht
    }
    

    Möglicher Lösungsweg oder wieder Fehlversuch?



  • RussianTux schrieb:

    if(data != 0)
                delete data;
    

    delete nimmt gerne Nullzeiger entgegen.

    RussianTux schrieb:

    // "delete P1;" weggelassen da nicht erforderlich
    
        return 0;
        // objekt wird beim beenden automatisch gelöscht
    }
    

    Wird es nicht.

    Richtig wäre es so:

    class Pointer
    {
    private:
        std::unique_ptr<Data> data;
    public:
    
        Pointer(Data new_data)
    		: data(new Data(new_data))
        {
        }
    };
    
    int main(void)
    {
        Data d;
        std::unique_ptr<Pointer> P1(new Pointer(d));
    
        // "delete P1;" weggelassen da nicht erforderlich
    
        return 0;
        // objekt wird beim beenden automatisch gelöscht
    }
    


  • TyRoXx schrieb:

    RussianTux schrieb:

    if(data != 0)
                delete data;
    

    delete nimmt gerne Nullzeiger entgegen.

    RussianTux schrieb:

    // "delete P1;" weggelassen da nicht erforderlich
    
        return 0;
        // objekt wird beim beenden automatisch gelöscht
    }
    

    Wird es nicht.

    SeppJ schrieb:

    int main(void)
    {
    Pointer* P1 = new Pointer;

    //...

    delete P1;
    }

    Dies ist totaler Mist. Wenn ein new und ein dazugehöriges delete im gleichen Scope stehen, dann ist dies das gleiche wie ein Objekt mit automatischer Speicherklasse, bloß in schlecht (sowohl Performance als auch Probleme beim unerwartetem Verlassen des Scopes). Kommst du von Java?

    [quote="TyRoXx"]

    RussianTux schrieb:

    Richtig wäre es so:

    class Pointer
    {
    private:
        std::unique_ptr<Data> data;
    public:
       
        Pointer(Data new_data)
    		: data(new Data(new_data))
        {
        }
    };
    
    int main(void)
    {
        Data d;
        std::unique_ptr<Pointer> P1(new Pointer(d));
    
        // "delete P1;" weggelassen da nicht erforderlich
    
        return 0;
        // objekt wird beim beenden automatisch gelöscht
    }
    

    std::unique_ptr<T> kommt hier nicht infrage, das ist eine Übungsaufgabe dessen Aufwand nicht gleich die Standard-lib beansprucht, es geht darum zu verstehen was genau hier abläuft


  • Mod

    RussianTux schrieb:

    Möglicher Lösungsweg oder wieder Fehlversuch?

    So ziemlich alles falsch.

    Zeile 11: Initialisierungsliste benutzen, dann wird auch bei Fehler sauber abgeräumt.
    Zeile 16: Beschwör lieber ein paar Voodoogötter, das bringt mehr.
    Klasse Pointer: Regel der großen Drei verletzt.
    Zeile 26: Falscher Unsinn.
    Zeile 29: Falscher, gefährlicher Unsinn.

    Aus deiner Antwort auf TyRoXx entnehme ich, dass du nicht weißt, was automatische Speicherklasse ist.



  • RussianTux schrieb:

    std::unique_ptr<T> kommt hier nicht infrage, das ist eine Übungsaufgabe dessen Aufwand nicht gleich die Standard-lib beansprucht, es geht darum zu verstehen was genau hier abläuft

    In C++ läuft das aber genau so ab. delete ist nicht dazu da, direkt verwendet zu werden.
    Was ist eigentlich so schwer daran zu verstehen, dass unique_ptr bei seiner Zerstörung delete aufruft?


  • Mod

    TyRoXx schrieb:

    RussianTux schrieb:

    if(data != 0)
                delete data;
    

    delete nimmt gerne Nullzeiger entgegen.

    Allerdings ist es sinnlos, etwas zu löschen, das gar nicht da ist. Keine Ahnung warum das immer das Erste ist, was sinnlos kommentiert wird.
    Die Abfrage wird dann falsch, wenn data an dieser Stelle gar nicht 0 sein kann/darf.
    Ist eine Abfrage nach diesen Kriterien sinnvoll, kann das Weglassen sogar zu einer (geringen) Verschlechterung der Performance führen. Einen Aufruf der Deallaktionsfunktion kann der Compiler nämlich in der Regel selbst dann nicht wegoptimieren, wenn der Compiler weiß, dass der Zeiger 0 sein wird. Umgekehrt ist der Performanceverlust einer zusätzlichen solchen Abfrage im Verhältnis zu den tatsächlichen Kosten der Deallokation in den Fällen, in denen diese durchgeführt werden muss, vernachlässigbar.



  • SeppJ schrieb:

    RussianTux schrieb:

    Möglicher Lösungsweg oder wieder Fehlversuch?

    So ziemlich alles falsch.

    Zeile 11: Initialisierungsliste benutzen, dann wird auch bei Fehler sauber abgeräumt.

    Irgendwas in der Initialisierungsliste zu machen ist hier weder notwendig noch bringt es irgendwas.
    Rohe Zeiger bleiben dumm, auch wenn man sie in der Initialisierungsliste initialisiert.


  • Mod

    camper schrieb:

    TyRoXx schrieb:

    RussianTux schrieb:

    if(data != 0)
                delete data;
    

    delete nimmt gerne Nullzeiger entgegen.

    Allerdings ist es sinnlos, etwas zu löschen, das gar nicht da ist. Keine Ahnung warum das immer das Erste ist, was sinnlos kommentiert wird.
    Die Abfrage wird dann falsch, wenn data an dieser Stelle gar nicht 0 sein kann/darf.
    Ist eine Abfrage nach diesen Kriterien sinnvoll, kann das Weglassen sogar zu einer (geringen) Verschlechterung der Performance führen. Einen Aufruf der Deallaktionsfunktion kann der Compiler nämlich in der Regel selbst dann nicht wegoptimieren, wenn der Compiler weiß, dass der Zeiger 0 sein wird. Umgekehrt ist der Performanceverlust einer zusätzlichen solchen Abfrage im Verhältnis zu den tatsächlichen Kosten der Deallokation in den Fällen, in denen diese durchgeführt werden muss, vernachlässigbar.

    Ich wette das ist nicht das was RussianTux sich gedacht hat. RussianTux denkt nämlich bestimmt, dass new den Wert 0 zurück geben würde, wenn ein Fehler auftritt. Was natürlich nicht der Fall ist.


  • Mod

    hustbaer schrieb:

    SeppJ schrieb:

    RussianTux schrieb:

    Möglicher Lösungsweg oder wieder Fehlversuch?

    So ziemlich alles falsch.

    Zeile 11: Initialisierungsliste benutzen, dann wird auch bei Fehler sauber abgeräumt.

    Irgendwas in der Initialisierungsliste zu machen ist hier weder notwendig noch bringt es irgendwas.
    Rohe Zeiger bleiben dumm, auch wenn man sie in der Initialisierungsliste initialisiert.

    Wenn new hier eine Exception schmeißt, wird der Destruktor aufgerufen und es kracht, weil der Zeiger irgendeinen undefinierten Wert hat. Wenn eine Initialisierungsliste benutzt würde, dann würde korrekt abgeräumt.


  • Mod

    SeppJ schrieb:

    camper schrieb:

    TyRoXx schrieb:

    RussianTux schrieb:

    if(data != 0)
                delete data;
    

    delete nimmt gerne Nullzeiger entgegen.

    Allerdings ist es sinnlos, etwas zu löschen, das gar nicht da ist. Keine Ahnung warum das immer das Erste ist, was sinnlos kommentiert wird.
    Die Abfrage wird dann falsch, wenn data an dieser Stelle gar nicht 0 sein kann/darf.
    Ist eine Abfrage nach diesen Kriterien sinnvoll, kann das Weglassen sogar zu einer (geringen) Verschlechterung der Performance führen. Einen Aufruf der Deallaktionsfunktion kann der Compiler nämlich in der Regel selbst dann nicht wegoptimieren, wenn der Compiler weiß, dass der Zeiger 0 sein wird. Umgekehrt ist der Performanceverlust einer zusätzlichen solchen Abfrage im Verhältnis zu den tatsächlichen Kosten der Deallokation in den Fällen, in denen diese durchgeführt werden muss, vernachlässigbar.

    Ich wette das ist nicht das was RussianTux sich gedacht hat. RussianTux denkt nämlich bestimmt, dass new den Wert 0 zurück geben würde, wenn ein Fehler auftritt. Was natürlich nicht der Fall ist.

    Vermutlich. Nur fällt das eben unter die Kategorie "Zeiger kann gar nicht 0 sein" und damit ist TyrOXx Begründung schlicht falsch.
    Ich bin dafür, diese Abfrage zu eleminieren, wo sie sinnlos ist. Ich habe aber etwas gegen die Begründung dieses Vorhabens damit, dass delete auch Nullzeiger nimmt.


Anmelden zum Antworten