Zeiger = 0;


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



  • SeppJ schrieb:

    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.

    Nö.
    Es werden nur vollständig konstruierte Objekte zerstört
    Und als vollständig konstruiert gilt das Objekt erst, wenn der Konstruktor regulär verlassen wurde (=ohne eine Exception zu werfen).

    Deswegen muss man sich ja in C++ auch für jeden Fliegenschiss eigene RAII Helper-Klassen basteln.



  • camper schrieb:

    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.

    In den allermeisten Fällen finde ich die Begründung dass delete auch Nullzeiger nimmt vollkommen ausreichend.

    Wenn der "Normalfall" der ist, dass gelöscht werden muss, spielt die Performance keine Rolle (wer optimiert schon auf den Fehlerfall?), und man spart sich eine Zeile = übersichtlicher = juchui.

    Bzw. auch wenn es ganz regulär vorkommen kann, aber der minimale Performance-Vorteil der "mit if" Variante einfach nicht ins Gewicht fällt: eine Zeile gespart -> juchui.

    In Fällen wo es häufig vorkommen kann dass der Zeiger wirklich Null ist, und in denen der Performanceunterschied möglicherweise nicht irrelevant ist kann bzw. sollte man das "if" stehen lassen.

    Und nochwas: DER #1 Grund warum Leute das "if" schreiben ist weil sie nicht wissen dass delete auch Nullzeiger nimmt. Daher finde ich den Hinweis darauf immer gerechtfertigt.


  • Mod

    hustbaer schrieb:

    camper schrieb:

    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.

    In den allermeisten Fällen finde ich die Begründung dass delete auch Nullzeiger nimmt vollkommen ausreichend.

    Wenn der "Normalfall" der ist, dass gelöscht werden muss, spielt die Performance keine Rolle (wer optimiert schon auf den Fehlerfall?), und man spart sich eine Zeile = übersichtlicher = juchui.

    Bzw. auch wenn es ganz regulär vorkommen kann, aber der minimale Performance-Vorteil der "mit if" Variante einfach nicht ins Gewicht fällt: eine Zeile gespart -> juchui.

    Dann ist das der Grund: Übersichtlichkeit und kürzerer Code. Das sollte dann auch so geschrieben werden. Das liegt nämlich - jedenfalls für micht - nicht so eindeutig auf der Hand. So gibt man die schöne Symmetrie zwischen erfolgreicher Allokation und Deallokation auf. Das macht es nicht unbedingt einfacher, die Korrektheit zu überprüfen.

    hustbaer schrieb:

    In Fällen wo es häufig vorkommen kann dass der Zeiger wirklich Null ist, und in denen der Performanceunterschied möglicherweise nicht irrelevant ist kann bzw. sollte man das "if" stehen lassen.

    Persönlich ist mir das Performance-Argument ziemlich egal, ich habe es nur gebracht, weil sich manche Leute davon überzeugen lassen. In den Fällen wo es relevant werden kann (Smartpointer), weiß man das in der Regel schon.

    hustbaer schrieb:

    Und nochwas: DER #1 Grund warum Leute das "if" schreiben ist weil sie nicht wissen dass delete auch Nullzeiger nimmt. Daher finde ich den Hinweis darauf immer gerechtfertigt.

    Es besteht allerdings immer noch ein Unterschied zwischen DIngen, die man bloß tun kann und solchen, die man tun sollte.

    TyRoXx schrieb:

    RussianTux schrieb:

    if(data != 0)
                delete data;
    

    delete nimmt gerne Nullzeiger entgegen.

    Ist für mich eine Aufforderung, das if wegzulassen. Die bloße Möglichkeit, das tun zu können, ist eben kein Grund, es zu tun. Sein->Sollen = Fehlschluss.



  • @camper
    Was du schreibst stimmt alles. Was ich aber nicht rauslesen konnte, ist wann du jetzt persönlich empfehlen würdest das "if" zu schreiben und wann nicht. Bzw. wann du es selbst schreibst und wann nicht.

    Ich schreibe es nur in Fällen wo es einen relevanten Performance-Unterschied geben könnte.

    Und zwar hauptsächlich deswegen, weil es eben redundant ist.

    Das "if" kommuniziert für mich "der Pointer könnte NULL sein". Und das impliziert für mich wiederum "überall wo kein if steht ist er garantiert nicht NULL". Und das wiederum ist in Projekten an denen mehrere Leute arbeiten einfach nicht wahr. Kurz: es ist irreführend.


  • Mod

    hustbaer schrieb:

    @camper
    Was du schreibst stimmt alles. Was ich aber nicht rauslesen konnte, ist wann du jetzt persönlich empfehlen würdest das "if" zu schreiben und wann nicht. Bzw. wann du es selbst schreibst und wann nicht.

    Es ist größtenteils irrelevant, also gebe ich keine Empfehlung. Persönlich würde ich die Abfrage immer dann verwenden, wenn der Zeiger 0 sein könnte, wenn denn
    - ich überhaupt soviel programmieren würde, und
    - dabei auch noch explizite delete benutzen würde.
    In einem Beitrag fürs Forum lasse ich es i.d.R. weg, weil hier der Vorteil der Kürze klar überwiegt.

    imo ist hier eine Empfehlung ungefähr genauso sinnlos wie die, bei main ein return zu schreiben (oder es wegzulassen). Der Unterschied wird nie ausreichen, um eine Änderung bestehenden Codes nur aus diesem Grund rechtfertigen zu können.

    hustbaer schrieb:

    Das "if" kommuniziert für mich "der Pointer könnte NULL sein". Und das impliziert für mich wiederum "überall wo kein if steht ist er garantiert nicht NULL". Und das wiederum ist in Projekten an denen mehrere Leute arbeiten einfach nicht wahr. Kurz: es ist irreführend.

    Verstehe das Argument nicht. Natürlich kannst du dich auf die Implikation eines bestimmten Stils nicht verlassen, wenn dieser Stil im Projekt nicht praktiziert wird.



  • Wenn es schon C++03 und keine STL sein soll, dann bau doch einfach einen shared_ptr für Arme.
    Zb soetwas wäre doch recht solide:

    template<typename ValueT>
    class shared_ptr
    {
    private:
        struct ptr_storage
        {
            ptr_storage(ValueT* ptr = 0, std::size_t ref_count = 0)
                : ptr(ptr), ref_count(ref_count)
            { }
    
            ValueT* ptr;
            std::size_t ref_count;
        }* m_storage;
    
    public:
        shared_ptr()
            : m_storage(0)
        { }
    
        shared_ptr(ValueT* ptr)
            : m_storage(ptr != 0 ? new ptr_storage(ptr, 1) : 0)
        { }
    
        shared_ptr(shared_ptr const& other)
            : m_storage(other.m_storage)
        {
            if(m_storage)
                ++m_storage->ref_count;
        }
    
        ~shared_ptr()
        {
            if(m_storage)
            {
                if(!(--m_storage->ref_count))
                {
                    delete m_storage->ptr;
                    delete m_storage;
                }
            }
        }
    
        shared_ptr<ValueT>& operator=(shared_ptr<ValueT> other)
        {
            swap(other);
        }
    
        void swap(shared_ptr<ValueT>& other)
        {
            ptr_storage* helper = other.m_storage;
            other.m_storage = m_storage;
            m_storage = helper;
        }
    
        ValueT* get()
        {
            return m_storage->ptr;
        }
    
        ValueT* operator->()
        {
            return get();
        }
    
        ValueT& operator*()
        {
            return *get();
        }  
    };
    


  • Ich persönlich würde auch 0-Zeigern einen Storage mit einem 0-Zeiger als Pointer sowie einem Refcount von 1 geben, das beseitigt ein paar Sonderbehandlungen. Ansonsten fehlt das "return *this;" in deinem Zuweisungsoperator und dein get() sowie die beiden überladenen Pointer-Operatoren sollten const sein. 😉
    Und swap kannste über std::swap implementieren.



  • War ja nur kurz heruntergetippt. 😉
    std::swap ist bewusst nicht drin, da STL.



  • Kellerautomat schrieb:

    Ich persönlich würde auch 0-Zeigern einen Storage mit einem 0-Zeiger als Pointer sowie einem Refcount von 1 geben, das beseitigt ein paar Sonderbehandlungen.

    Massive Performance-Verschwendung.



  • Warum wird der Destruktur beim verlassen des Blocks nicht aufgerufen?!

    Ich meine:

    int main(void)
    {
        int Integer = 4;
        std::cout << "Integer = " << Integer << "\n";
    
        //wir rufen hier ja auch nicht den Destruktor manuell auf?
        return 0;
    }
    

    Warum muss das bei Zeigern denn gemacht werden? Das verstehe ich nicht, könnte es mir jemand genauer erklären?



  • Nun ja, der Zeiger wird beim Verlassen des Scopes ordnungsgemäß zerstört, wie du es von Integern kennst. Aber wieso sollte dabei das Objekt, auf das gezeigt wird, zerstört werden?



  • Nicht zu fassen was für eine bescheuerte Diskussion hier geführt wird bezüglich delete . Einfach nicht zu fassen. Da fehlen mir die Worte.

    Vielleicht hilft das weiter: Ein Zeiger ist nur eine Zahl, die angibt, wo das Objekt im Speicher zu finden ist. new reserviert dir Platz für ein Objekt und gibt dir einen Zeiger, um das Objekt zu benutzen. Wenn du es nicht mehr brauchst, solltest du delete benutzen, um den Platz des Objektes wieder freizugeben.
    Ein Zeiger an sich tut gar nichts und wird automatisch zerstört, wie andere Primitive Typen wie z. B. int auch.



  • TyRoXx schrieb:

    Nicht zu fassen was für eine bescheuerte Diskussion hier geführt wird bezüglich delete . Einfach nicht zu fassen. Da fehlen mir die Worte.

    Eigentlich ging die Diskussion hauptsächlich um das if vor dem delete . Aber du darfst mir gerne verraten, was dich so fassungslos gemacht hat.



  • hustbaer schrieb:

    Massive Performance-Verschwendung.

    Wie oft hat man denn Nullzeiger?



  • TyRoXx schrieb:

    Nicht zu fassen was für eine bescheuerte Diskussion hier geführt wird bezüglich delete . Einfach nicht zu fassen. Da fehlen mir die Worte.

    Vielleicht hilft das weiter: Ein Zeiger ist nur eine Zahl, die angibt, wo das Objekt im Speicher zu finden ist. new reserviert dir Platz für ein Objekt und gibt dir einen Zeiger, um das Objekt zu benutzen. Wenn du es nicht mehr brauchst, solltest du delete benutzen, um den Platz des Objektes wieder freizugeben.
    Ein Zeiger an sich tut gar nichts und wird automatisch zerstört, wie andere Primitive Typen wie z. B. int auch.

    Sehr gute Antwort, danke!

    Theoretisch wäre das die richtige Lösung:

    class Data;
    
    class Pointer
    {
    private:
    
        Data* data
    
    public:
    
        Pointer(Data& d)
        {
            data = new Data(d);
        }
    
        free_memory()
        {
            delete data;
        }
    };
    
    int main(void)
    {
        Data d;
        Pointer* p = new Pointer(d);
    
        p->free_memory();
    
        return 0;
    }
    

Anmelden zum Antworten