Hängende Zeiger vermeiden



  • Wie vermeide ich am besten hängende Zeiger mit modernen C++? In meinem Programm hatte ich heute einen Crash(zum Glück, sonst wäre mir der Fehler nicht aufgefallen). Ich habe die Problematik auf folgendes Beispiel runter brechen können:

    #include <vector>
    
    class B
    {
    public:
    	void doIt() {}
    };
    
    class A
    {
    public:
    	A(int value) : value_(value) 
    	{
    		b_ = new B();
    	}
    
    	~A()
    	{
    		delete b_;
    	}
    
    	B* b_;
    
    private:
    	int value_{ 0 };
    };
    
    int main()
    {
    	std::vector<A> vec;
    	for (int i = 0; i < 10; ++i) {
    		vec.push_back(A(42)); // von A wird eine Kopie erzeugt und in vec gespeichert 
    		                      // die Objekte in A werden dachnach durch den Aufruf des 
    		                      // Destruktors von A ungueltig 
    	}
    
    	vec[0].b_->doIt(); // crash, da der Zeiger auf b_ nicht mehr gueltig
    }
    
    

    Ich beherrsche nur einen ganz kleinen Teil von C++ und will mal die Fortgeschrittenen und Profis hier fragen wir ihr die Sache mit modernen C++(also bis C++17) lösen würdet?



  • Auf new und delete verzichten?!

    -> "Rule of Zero": s. The rule of three/five/zero



  • Das würde dann in meinem Beispiel wie aussehen?



  • Na, so:

    class A
    {
    public:
    	A(int value) : value_(value) 
    	{
    	}
    
    	B b_;
    
    private:
    	int value_{ 0 };
    };
    

    Und wenn du doch Zeiger benötigst, dann benutze Smartpointer, wie unique_ptr oder shared_ptr.



  • Ok, aber wenn b_ jetzt noch einen Parameter bei der Konstruktion mit übergeben werden müsste, den er im Konstruktor von A bekommt?



  • Also wenn das Beispiel nun so hier aussieht, dann kann ich ja nicht einfach das new und delete weglassen und das Objekt b_ als Membervariable erstellen, oder doch?

    #include <vector>
    
    class B
    {
    public:
    	B(int value) : value_(value) {}
    	void doIt() {}
    private:
    	int value_{ 0 };
    };
    
    class A
    {
    public:
    	A(int value) : value_(value) 
    	{
    		b_ = new B(value);
    	}
    
    	~A()
    	{
    		delete b_;
    	}
    
    	B* b_;
    
    private:
    	int value_{ 0 };
    };
    
    int main()
    {
    	std::vector<A> vec;
    	for (int i = 0; i < 10; ++i) {
    		vec.push_back(A(42)); // von A wird eine Kopie erzeugt und in vec gespeichert 
    		                      // die Objekte in A werden dachnach durch den Aufruf des 
    		                      // Destruktors von A ungueltig 
    	}
    
    	vec[0].b_->doIt(); // crash, da der Zeiger auf b_ nicht mehr gueltig
    }
    
    


  • Wäre dies dann die korrekte Lösung?

    #include <vector>
    #include <memory>
    
    class B
    {
    public:
    	B(int value) : value_(value) {}
    	void doIt() {}
    private:
    	int value_{ 0 };
    };
    
    class A
    {
    public:
    	A(int value) : value_(value) 
    	{
    		b_ = std::make_shared<B>(value);
    	}
    
    	~A()
    	{
    	}
    
    	std::shared_ptr<B> b_;
    
    private:
    	int value_{ 0 };
    };
    
    int main()
    {
    	std::vector<A> vec;
    	for (int i = 0; i < 10; ++i) {
    		vec.push_back(A(42)); // von A wird eine Kopie erzeugt und in vec gespeichert 
    		                      // die Objekte in A werden dachnach durch den Aufruf des 
    		                      // Destruktors von A ungueltig 
    	}
    
    	vec[0].b_->doIt(); // crash, da der Zeiger auf b_ nicht mehr gueltig
    }
    
    


  • Warum nimmst du für B einen Zeiger, für value_ aber nicht?



  • Viel zu lernen du hast, mein Pandawa ;- )

    Stichwort: Initialisierungsliste

    class A
    {
    public:
    	A(int value) : value_(value), b_(value) 
    	{
    	}
    
    	B b_;
    
    private:
    	int value_{ 0 };
    };
    

    Bei dem anderen Member value_ hast du sie doch auch schon benutzt?!



  • Wohl wahr, danke für den Tipp. Irgendwann hatte ich mal da vieles von gelernt, aber reines C++ lernen und dann erst ein Projekt anfangen klappt bei mir nicht. Da vergesse ich alles weil es einfach nur stumpfes auswendig lernen ist und ich nie begriffen habe wofür und warum ich was brauche. Jetzt bei meinem Projekt stolpere ich automatisch in die Fallen mit Zeigern Objekten und Co und der Lerneffekt ist hundert mal höher.

    @manni66 value ist wirklich nur ein int, da reichte mir copy by value



  • @chris4cpp sagte in Hängende Zeiger vermeiden:

    @manni66 value ist wirklich nur ein int, da reichte mir copy by value

    Diese Aussage ist einfach nur Quatsch.



  • @chris4cpp sagte in Hängende Zeiger vermeiden:

    Wie vermeide ich am besten hängende Zeiger mit modernen C++?

    Indem Du keine Zeiger verwendest.

    Das war ein Gratistip. Jede weitere Auskunft € 0,90.



  • @manni66 sagte in Hängende Zeiger vermeiden:

    @chris4cpp sagte in Hängende Zeiger vermeiden:

    @manni66 value ist wirklich nur ein int, da reichte mir copy by value

    Diese Aussage ist einfach nur Quatsch.

    Na ich nehme oder übergebe Zeiger oder Referenzen wenn ich es mir nicht leisten will, dass jedes mal eine Kopie erzeugt wird(copy by value) Beim int wie value_ brauche ich keinen Zeigen, da hier ein copy by value genauso "teuer" von der Performance ist als wenn ich ein Zeiger übergeben würde.

    Was ist daran Quatsch?

    Vielleicht habe ich dich auch falsch verstanden.



  • @chris4cpp
    Die Kopie wird wann und wo erzeugt und ist weshalb teuer?



  • void doIt(Objekt A); // teuer call by value
    void doIt(Objekt* A); // billig da nur ein Zeiger übergeben wird call by reference
    void doIt(int A); // billig 
    void doIt(int* A); // genauso billig
    

    Was ist daran Quatsch?



  • @chris4cpp sagte in Hängende Zeiger vermeiden:

    void doIt(Objekt A); // teuer call by value
    void doIt(Objekt* A); // billig da nur ein Zeiger übergeben wird call by reference
    void doIt(int A); // billig 
    void doIt(int* A); // genauso billig
    

    Was ist daran Quatsch?

    Willst du mich verarschen?



  • Hast du noch nie von call by value und call by reference gehört? Was soll zudem der Ton, man kann sich auch zivilisiert unterhalten. Stell doch mal richtige Fragen und schmeiße nicht nur Fetzen in den Raum.

    Ich gehe davon aus, dass ich deine Frage Falsch verstanden haben, aber darauf wird gar nicht eingegangen.



  • Du betrachtest nur die Kopie. Ein int (der üblicherweise Maschinenwortbreite hat) ist genauso billig/teuer zu kopieren wie ein Zeiger (der üblicherweise Manschinenwortbreite hat). Was Du nicht beachtest sind die Kosten der Dereferenzierung.



  • @chris4cpp sagte in Hängende Zeiger vermeiden:

    void doIt(Objekt A); // teuer call by value
    void doIt(Objekt* A); // billig da nur ein Zeiger übergeben wird call by reference
    void doIt(int A); // billig 
    void doIt(int* A); // genauso billig
    

    Was ist daran Quatsch?

    Das ist hier was ganz anderes, als zu Beginn des Threads. Hier geht es um den Informationsaustausch zwischen Funktionen, ursprünglich ging es um Datenhaltung in einem Objekt Deiner Klasse.
    Hier, bei Herumreichen zwischen Funktionen würde man eine Referenz einem Zeiger vorziehen.



  • Ich hatte für B einen Zeiger genommen weil ich nicht wusste, dass man auch über die Initialisierungsliste das Objekt B mit einem anderen Konstruktor als dem Defaultkonstruktor erstellen kann. Mein Problem hat sich damit erledigt. Also Bonus habe ich mein Projekt daraufhin überarbeitet und alle Zeiger raus genommen(bis auf Qt Sachen) die nicht nötig waren, carrays durch std::array ersetzt und da wo ich Zeiger brauche, weil ich z.B. eine Basisklasse habe und diese als Datentyp angebe, aber eine abgeleitet Klasse davon dann erstelle, dort habe ich Smartpointer genommen. Also hat der Thread doch einiges gebracht.

    Was mich geärgert hat:
    Die Frage von manni hatte mich verwirrt und in eine völlig andere Richtung gebracht. Aber die Situation aufzuklären war anscheinend nicht sein Ziel, das hat mich sehr geärgert. Ich dachte er meint die Parameterübergabe mit call by value und call by reference, die dann auf einmal als Quatsch abgetan wurde, als wenn es so was nicht gäbe. Habe dann nochmal gegoogelt aber natürlich gibt es die Übergabe per call by value und call by reference, es war also kein Quatsch. Genauso hätte ich googeln können ob Laubbäume oft grüne Blätter haben. Mir war ja schon klar, dass ich da was missverstanden haben, aber darauf wurde null eingegangen, sondern dann kam nur so eine Beleidigung. Da habe ich mich kurz gefragt was die Motivation eines Menschen sein kann so zu reagieren? Ich bin aber zu alt um mich lange mit Energiefressern zu beschäftigen.

    Wie dem auch sei. Ich danke den Leuten, die mir hier weiter geholfen haben. Vielleicht stelle ich hier doch noch mal eine Fachfrage.


Log in to reply