Zeiger = 0;


  • 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;
    }
    


  • @Kellerautomat
    Ach, recht oft eigentlich.
    z.B. jedes mal wenn man den Zeiger default-konstruiert und danach erst zuweist.
    Manchmal hat man auch optionale "Teile" in einem Objekt.

    Und davon abgesehen ist es praktisch wenn der Default-Konstruktor von solchen Klassen no-throw ist.



  • Hm, vielleicht haste wirklich Recht, ok.
    Wirklich überzeuugt hat mich das no-throw Argument. Andererseits finde ich es auch seltsam, wenn folgendes Konstrukt etwas werfen könnte:

    vector<shared_ptr<int>> v(100);
    v[42] = new int; // kein Speicher mehr für interne Verwaltungsdaten, kabumm. int geleakt.
    

  • Mod

    Kellerautomat schrieb:

    Hm, vielleicht haste wirklich Recht, ok.
    Wirklich überzeuugt hat mich das no-throw Argument. Andererseits finde ich es auch seltsam, wenn folgendes Konstrukt etwas werfen könnte:

    vector<shared_ptr<int>> v(100);
    v[42] = new int; // kein Speicher mehr für interne Verwaltungsdaten, kabumm. int geleakt.
    

    Dann muss der Zuweisungsoperator im Fehlerfall delete aufrufen. Bei std::shared_ptr geht das sowieso nur über den Konstruktor

    template<class Y> explicit shared_ptr(Y* p);
    ...
    7 Exception safety: If an exception is thrown, delete p is called.



  • Im russischen gibt es solch ein Sprichwort: "jeder spricht darüber, was ihm weh tut" und antwortet nicht auf die Frage des anderen.

    Meine Frage war, wie ich das ohne diesen ganzen Schnick-Schnack ganz simpel geregelt bekomme.

    oben habe ich ein korrigiertes Code-Stück aufgetipt wenn ihr das noch nicht bemerkt habt:

    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; 
    }
    

    Beide Pointer werden ordnungsgemäß gelöscht und deren Inhalt am Ende geleert, müsste doch korrekt funktionieren?



  • RussianTux schrieb:

    Beide Pointer werden ordnungsgemäß gelöscht und deren Inhalt am Ende geleert, müsste doch korrekt funktionieren?

    Ich sehe da zwei new und nur ein delete .



  • @RussianTux: Du baust dir doch gerade selbst einen Smartpointer nach, nur viel viel schlechter. Das ist kein Schnick-Schnack, vertrau doch den erfahrenen Leuten hier. Du löschst das Pointer-Objekt nicht und dadurch, dass du free_memory aufrufen musst, ist das ganze auch nicht Exception-save.



  • TyRoXx schrieb:

    RussianTux schrieb:

    Beide Pointer werden ordnungsgemäß gelöscht und deren Inhalt am Ende geleert, müsste doch korrekt funktionieren?

    Ich sehe da zwei new und nur ein delete .

    Ups, mein Fehler, korrigiere ich mal eben:

    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(); 
        delete p;
    
        return 0; 
    }
    


  • Was bringt dir die Pointer -Klasse, wenn sie nicht automatisch aufräumt?

    Wenn man free_memory() explizit aufrufen muss, kann man genauso gut delete verwenden. Dadurch gewinnst du absolut nichts.



  • Die ordentliche Lösung ohne Schnickschnak wäre std::unique_ptr.


Anmelden zum Antworten