shared_ptr - Delete?



  • manchmal seid ihr hier echt ein bisschen unfair.
    Ja schön, wirkt wirklich ziemlich naiv, new/Delete-fehler durch noch mehr new's zu beheben, aber das ist kein Grund hier so am Rad zu drehen.
    Zumal wenn ich das richtig verstanden habe, es keinen Unterschied macht, weil die shared_ptr sicher nicht anfangen zu leaken.



  • Doch, eine Fehlerquelle gibt es:

    some_func(shared_ptr<Foo>(new Foo()), shared_ptr<Bar>(new Bar()), func());
    

    Hier kann es passieren, dass erst 2x new aufgerufen wird (bzw. einmal new + Konstrukotor, dann das nächste), aber die Speicheradressen werden noch nicht an den shared_ptr gekettet. Wirft jetzt der zweite Konstruktor oder die func()-Funktion eine Exception, dann hast du dein Leak.

    some_func(make_shared<Foo>(), make_shared<Bar>(), func());
    

    Hat das Problem nicht mehr.



  • cl90 schrieb:

    Zumal wenn ich das richtig verstanden habe, es keinen Unterschied macht, weil die shared_ptr sicher nicht anfangen zu leaken.

    Es stimmt, dass hared_ptr recht sicher sind. Das ändert aber nichts daran, dass es Blödsinn ist Objekte dynamisch zu erstellen, wenn das nicht nötig ist. Das ganze ist langsamer, unkomfortabler und du handelst dir unnötige Indirektionen ein. Am besten sowas gar nicht erst angewöhnen.



  • Skym0sh0 schrieb:

    Doch, eine Fehlerquelle gibt es:

    some_func(shared_ptr<Foo>(new Foo()), shared_ptr<Bar>(new Bar()), func());
    

    also wird sowas hier vermutlich auch probleme machen oder?

    m_Commands.insert(_Command("set_screen_width",			shared_ptr<_IStrategy>(new _cmdScreenWith()),	shared_ptr<_IStrategyVal>(new _valScreenWidth())));
    

    TNA schrieb:

    Am besten sowas gar nicht erst angewöhnen.

    Ok, Ich habe vorher mit Raw Pointer gearbeitet. Mir wurde nur abgeraten das weiter zu machen.
    So wie ich das mitbekommen habe war ein oder mehrere smart pointer nur in den neuen c++ standarts vorhanden.
    Was gäbe es denn für alternativen?

    Ich vermeide Call by Value immer wenn irgendetwas übergeben wird das größer ist als 4 byte. Ich habe mir in vielen fällen das "const&" angewöhnt. Aber gerade bei Dingen wie Datenklassen die an jede klasse weiter gereicht werden, kam mir das shared_ptr Konzept sinvoll vor.

    edit:
    bzw wenn ich eine lösung für folgendes problem bekomme würde ich sehr viel new spaaren:

    Wie kann ich in folgendem Code, die erstellung von m_iKin ohne irgendein new schreiben? Bemerkung: Der Konstruktor von iKin bekommt mehrere Parameter.

    class leg : public Observer		
    {								
    	private:					
    		shared_ptr<iKin_math>	m_iKin;
    };
    
    leg :: leg()
    {
    	m_iKin		= shared_ptr<iKin_math>(new iKin_math(...Vars...));
    }
    


  • cl90 schrieb:

    Wie kann ich in folgendem Code, die erstellung von m_iKin ohne irgendein new schreiben?

    class leg : public Observer		
    {								
      private:					
        iKin_math	m_iKin;
    };
    
    leg :: leg()
    : m_iKin(...Vars...)
    {
    }
    


  • ah 😑
    da war ja sowas wie eine initialisierungsliste... jetzt wo der Wink mit dem Zaunpfahl kommt errinnere ich mich.

    danke dir!



  • Ich verstehe nicht, wieso Du immer mit Raw-Pointer argumentierst, wenn man Dir von shared_ptr abrät. Weißt Du nicht, dass es in C++ automatische Objekte gibt wie in krümelkackers Code vorgestellt? Das ist weder Raw- noch Smart-Pointer. Wenn das Beispiel nur irgendwie illustriert ist und gar nicht den eigentlichen Code widerspiegelt, okay. Aber in dem Beispiel sind weder Raw-Pointer noch Smart-Pointers empfehlenswert.

    Ich habe mir in vielen fällen das "const&" angewöhnt. Aber gerade bei Dingen wie Datenklassen die an jede klasse weiter gereicht werden, kam mir das shared_ptr Konzept sinvoll vor.

    "Ich fahre eigentlich gern mit dem Auto zur Arbeit, aber für die Uni erschien es mir besser die schwarze Hose zu tragen."

    shared_ptr sind dazu gedacht Besitzverhältnisse zu klären, geteilte nämlich. Die werden nicht genutzt, um die Übergabe an Funktionen/Klassen anders zu handhaben. Einer Klasse, die einen Verweis (ohne Besitzmacht!) über ein Objekt benötigt, einen Raw-Pointer zu übergeben, ist in Ordnung. An dieser Stelle geht es aber auch nicht um Besitz, denn die Klasse, welche das Argument erhält, muss sich ja nicht um new/delete/Speicherverwaltung kümmern.

    Der Unterschied mag fein erscheinen, es ist aber wichtig sich darüber Gedanken zu machen, ob geteilter Besitz wirklich notwendig ist (und das ist eben fast nie der Fall). Wenn viele unterschiedliche Klassen ein Objekt nutzen, heißt das nicht, dass die das auch besitzen müssen.

    da war ja sowas wie eine initialisierungsliste...

    Das ist aber nicht der Punkt. Automatisches Objekt auf dem Stack wird hier genutzt, während Dein Code ein Objekt auf dem Heap (über new) abgelegt hat. Die Initialisierungsliste kannst Du auch nutzen, um letzteres zu erzeugen.

    Entweder lassen sich in Deinem Denken noch einige Dinge verbessern oder es liegt nur am Ausdruck Deiner Argumente. Jedenfalls gibt es da was zu tun. 🙂



  • Eisflamme schrieb:

    Jedenfalls gibt es da was zu tun. 🙂

    Das ist der Plan. Das programm läuft Absturzsicher und macht keine Probleme, nur ich will a) die Leaks loswerden denn das kann vermutlich zu einem Problem werden, und b) "unsauberen" code durch "besseren" ersetzen.

    Den Ansatz das raw pointer schlecht sind hab ich mitlerweile verworfen.

    Ich werd hier nochmal alles durchlesen und versuchen alle Tipps anzuwenden.



  • cl90 schrieb:

    Ok, Ich habe vorher mit Raw Pointer gearbeitet. Mir wurde nur abgeraten das weiter zu machen.

    Das Problem sind nicht die Raw-Pointer, das Problem sind Raw-Pointer in Verbindung mit new . Denn bei denen musst du manuell delete aufrufen. Raw-Pointer, die auf ein Objekt zeigen, welches an anderer Stelle verwaltet wird (z.B. durch Smart-Pointer, oder weil es auf dem Stack liegt) sind kein Problem.



  • Das Problem ist, das man hier Anfängern shared_ptr und Konsorten um die Ohren haut, bevor sie überhaupt verstanden haben, was das Problem ist.



  • Das Problem ist, das man hier Anfängern shared_ptr und Konsorten um die Ohren haut, bevor sie überhaupt verstanden haben, was das Problem ist.

    Also müssen sie erst verstehen, dass das Problem ist, dass man hier Anfängern shared_ptr und Konsorten um die Ohren haut, bevor sie überhaupt verstanden haben, was das Problem ist. 🤡



  • Im Prinzip ist die Geschichte mit automatisch delete aufrufen relativ überflüssig, wenn keine Exceptions im Spiel sind. Denn dann ist es noch überschaubar, an welchen Stellen Speicher deletet werden muss. Das bedeutet nicht, dass das dann schön ist oder keinerlei Fehler(Memory Leaks) auftreten können, aber in der Theorie würde das reichen.

    Sobald aber Exceptions dazukommen geht die Ressourcen-Verwaltung nur noch sinnvoll und übersichtlich mit RAII.

    Daher, vllt anfangs mal 2-3 Übungen machen mit new+delete, danach alles in RAII-Klassen einkapseln!



  • Ja ich versteh ja auch euren Standpunkt. Nur leider ist es an meiner Uni so das die Profs sich nicht darum scheren woher du die Sprache kennst, du sollst nur Ihre Projekte machen können. Das Man dann nicht die Zeit hat sich ein gutes Buch zur Hand zu nehmen und bei 0 Anzufangen ist leider vorprogrammiert.
    Ich bin auch direkt eingestiegen, und hab mich durch Trial and Error "Weiterentwickelt". Und solche Grundlagen fehlen dann leider manchmal.

    Die ganzen Pointer/shared_ptr sachen waren übrigents gar nicht das Problem...
    Das Problem liegt in der Allokierung der DirectX Ressourcen. Texturen und Meshes... Ich muss mich jetzt erstmal einlesen wie die Strukturen aufgebaut sind damit ich weiß wie ich die wieder löschen kann.



  • DirectX verwendet COM Objekte.
    Löschen tut man diese nicht, man gibt nur "seine" Referenz auf sie frei. Und wenn das die letzte Referenz war, dann löscht sich das Objekt von selbst.

    Zum Referenz-Zählen verwendet man ->AddRef() und ->Release(). Bzw. spezialisierte Smart-Pointer Klassen die einem das abnehmen.



  • Skym0sh0 schrieb:

    Im Prinzip ist die Geschichte mit automatisch delete aufrufen relativ überflüssig, wenn keine Exceptions im Spiel sind. Denn dann ist es noch überschaubar, an welchen Stellen Speicher deletet werden muss. Das bedeutet nicht, dass das dann schön ist oder keinerlei Fehler(Memory Leaks) auftreten können, aber in der Theorie würde das reichen.

    Sobald aber Exceptions dazukommen geht die Ressourcen-Verwaltung nur noch sinnvoll und übersichtlich mit RAII.

    Daher, vllt anfangs mal 2-3 Übungen machen mit new+delete, danach alles in RAII-Klassen einkapseln!

    Wenn Exceptions im Spiel sind, dann erleichtern RAII und Smartpointer das Leben ungemein. Wenn keine Exceptions verwendet werden, dann erleichtern sie nur ein wenig, aber sie tun es doch. Also warum sollte man auf RAII und Smartpointer verzichten? Wobei ich mich auch frage, warum man auf Exceptions verzichten sollte.

    Wobei auch ohne Exceptions gibt es genügend Möglichkeiten, die Freigabe von Resourcen zu übersehen. Also nehme ich es zurück, dass RAII und Smartpointer ohne Exceptions das Leben nur ein wenig erleichtern. Sie tun es auch ohne Exceptions ungemein 😃 .



  • tntnet schrieb:

    Wobei auch ohne Exceptions gibt es genügend Möglichkeiten, die Freigabe von Resourcen zu übersehen.

    Wer geschickt programmiert, für den ist das auch ohne RAII übersichtlich (ohne Exceptions).

    10-Zeilen-Funktionen + Ressourcenmanagement von Verarbeitung trennen = übersichtlicher Code.



  • Nur ist das Problem, dass RAII schon vom Design her Fehler vermeidet. Programmierer sind Menschen, das heißt sie machen Fehler. Und das heißt, auch ihr Code tendiert zu Fehlerhaftigkeit. RAII macht es schwerer, Fehler zu machen. Es kann neue hervorrufen, aber eine noch größere Reihe von Fehlern wird damit behoben.



  • öhm...
    Mag mir das jemand erklären:

    void main()	{	// Erzeugt leaks
    		CData			data = CData();		
    	}
    	void main()	{	// Erzeugt keine leaks
    		CData	data();		
    	}
    	void main()	{	// Erzeugt keine leaks
    		CData*	data = new CData();
    		delete data;
    	}
    
    	// jetzt ein Konstruktor einer Klasse:
    	DirectX_Device :: DirectX_Device(CData* cdata) 
    	{	// Erzeugt leaks.
    		m_xfiles.push_back("plate.x");
    		m_xfiles.push_back("lowPolySphere.x");
    	}
    	DirectX_Device :: DirectX_Device(CData* cdata) 
    	{	// Erzeugt Keine leaks.
    		//m_xfiles.push_back("plate.x");
    		//m_xfiles.push_back("lowPolySphere.x");
    	}
    

    Das mit "data = CData()" ist mir neu..
    Und muss man Vectoren deleten?



  • void main() {   // Erzeugt leaks 
            CData           data = CData();     
        }
    

    Das kann gar nichts, denn es ist kein gültiges C++.
    Und selbst wenn du den Rückgabetyp zu int änderst, ist diese Variante genauso sicher wie die zweite.

    Und muss man Vectoren deleten?

    Nein. Deswegen sind sie u.a. so angenehm.



  • cl90 schrieb:

    void main()	{	// Erzeugt leaks
    		CData			data = CData();		
    	}
    

    Das sollte keine Leaks erzeugen. Vermutlich gibt es einen Bug im Copy-Konstruktor von CData . Das würde auch erklären, warum der Fall mit new keine Leaks erzeugt, denn dort wird nur der normale Konstruktor aufgerufen.

    Nebenbei: im zweiten Fall wird kein CData -Objekt angelegt, sondern eine Funktion ohne Parameter mit Rückgabewert CData deklariert. Was du vermutlich willst, ist

    CData data; // ohne ()
    

    Vermutlich erzeugt das auch keine Leaks, aus dem gleichen Grund wie im new -Fall.


Anmelden zum Antworten