shared_ptr - Delete?



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



  • ipsec schrieb:

    cl90 schrieb:

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

    Das sollte keine Leaks erzeugen. Vermutlich gibt es einen Bug im Copy-Konstruktor von CData .

    Deshalb erzeugt auch das push_back() Leaks, hast die Klasse kein Move und gibt der Copykonstruktor vorher erzeugte Handles oder was auch immer nicht frei, gibts bei jeder Kopie Leaks.



  • also:

    CData	data;
    

    Erzeugt auch leaks.

    Aber ich habe nur einen Konstruktor deklariert. Ich hab mit dem Copy Konstruktor nichts gedreht.

    CData sieht so aus:

    class CData 
    {
    	public:
    		CData();
    
    		// Sehr viele Funktionen nach Muster get/set
    		D3DXVECTOR3*		get_TargetPtr(int leg);	// bsp.
    
    	private:
    		// Die Entsprechenden Variablen zu get/set
    		int					m_ReadDelay;	//bsp.
    };
    
    CData :: CData()
    	:	m_UDPSocket(&m_Acceleration, &m_RollPitchYaw),
    		m_CIni()	// Erzeugt auch ohne die beiden Klassen Leaks.
    					// Ich geh also davon aus das die ok sind.	
    {
    
    	// initialisierungen. Alles ohne new
    }
    


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

    Dann erzeugt dieser Code-Schnipsel dadurch Leaks, dass der Konstruktor von CData aufgerufen wird...



  • Zeig doch Mal mehr von der Klasse.

    Ich kann mir vorstellen, dass die Regel der großen 3 (5) nicht eingehalten wurde, d.h. es gibt einen dtor, der irgendwo delete aufruft, jedoch sind operator= und copy-ctor eben nicht definiert. Das führt dann dazu, dass der referenzierte Speicher gelöscht wird.

    Oder heißt Leaks, dass irgendwas nicht freigegeben wird? Eigentlich würde das Programm ja dann ziemlich klar crashen, wenn meine Theorie oben stimmen würde.

    Kann auch sein, dass einfach das delete zum jeweiligen new fehlt, wenn weder copy-ctor, operator= noch dtor definiert sind, aber in ctor new aufgerufen wird.

    Eins von beidem wird's doch wohl sein, komm schon.



  • Ok. Also dann werde ich mal mehr Posten damit ihr euch ein größeres Bild machen könnt.

    denn CData hat jetzt grade zum leak finden absolut keinen inhalt:

    CData :: CData()
    	//:	m_UDPSocket(&m_Acceleration, &m_RollPitchYaw),
    	//	m_CIni()
    
    {
    	/*unsigned int i;
    
    //	Ctrl	///////////////////////////////////////////////////////////////////////////
    
    	m_mouseL		= false;
    	m_mouseR		= false;
    
    	m_wheel			= 0.0f;
    	m_X				= 400;
    	m_Y				= 400;
    
    	m_WASD_Toggle	= 1;
    	m_Toggle		= false;
    
    	for(i = 0; i < 256; i++)	m_keystate[i] = false; 
    
    	m_Acceleration	= D3DXVECTOR3(0, 0, 0); 
    	m_RollPitchYaw	= D3DXVECTOR3(0, 0, 0);
    
    //	Console	///////////////////////////////////////////////////////////////////////////
    
    	m_wireframe = false;
    //	View	///////////////////////////////////////////////////////////////////////////
    
    	m_viewDistance	= 50.0f;
    	m_viewRotHori	= 0;
    	m_viewRotVert	= 0;
    
    //	Leg		///////////////////////////////////////////////////////////////////////////
    
    	m_Length[0]	= 3.29f;
    	m_Length[1]	= 1.5f;
    	m_Length[2]	= 6.78f;
    	m_Length[3]	= 8.07f;
    
    	D3DXMatrixRotationZ		(&m_matRot90,		D3DXToRadian(-90));
    	D3DXMatrixScaling		(&m_matBodySize,	0.1f, 0.1f, 0.1f);
    	D3DXMatrixScaling		(&m_matAxisSize,	0.05f, 2.0f, 0.05f);
    	D3DXMatrixRotationY		(&m_matTurn,		D3DXToRadian(180));
    
    	D3DXMatrixTranslation	(&m_matTransSO,	   -m_Length[0], 0, 0);
    	D3DXMatrixTranslation	(&m_matTransVS,		0, m_Length[1], 0);
    	D3DXMatrixTranslation	(&m_matTransOS,		0, m_Length[2], 0);
    	D3DXMatrixTranslation	(&m_matTransUS,		0, m_Length[3], 0);
    
    //	General	///////////////////////////////////////////////////////////////////////////
    
    	m_KillThisProcess	= false;
    	m_ReadDelay			= 0;*/
    }
    

    Das Hauptprogramm sieht in wirklichkeit auch so aus:
    CData ist global. (weil ich es auch für Windowproc nutzen wollte.)

    // includes
    
    	CData			data = CData();	
    	//CData*		data = new CData();
    	//CData	data;
    	//DirectX_Device	dxdv(&data);
    
    #include "inc/win_api.h"
    
    int WINAPI WinMain(HINSTANCE hInstance,
                       HINSTANCE hPrevInstance,
                       LPSTR lpCmdLine,
                       int nCmdShow)
    {
    	//delete data;
    
    	_CrtDumpMemoryLeaks();
    
        return EXIT_SUCCESS;
    }
    

Anmelden zum Antworten