Fehler während der laufzeit!

Hallo Markus Gögele

Korrekt, du gibst im Destruktor der loop-Klasse die aktuelle rect und color Variabeln frei, allerdings alle anderen in draw_scene durch new_color und new_rect allozierten Versionen nicht. Zudem wird der Destruktor aufgerufen wenn man das Spiel beendet, neuer Speicher wird jedoch in jedem Durchgang des Frames alloziert.

Wo wir nun aber gerade schon beim Thema sind:
Du verwendest C++ und du arbeitest direkt mit Zeigern (rectangle*, color*). Das beisst sich eigentlich. Zeiger sind ein Relikt aus C-Zeiten (genau so wie Arrays) und sollten nicht mehr verwendet werden.

In deinem Fall sähe das zum Beispiel so aus:
In der Loop-Klasse sind new_color und new_rect modifiziert:
C++:
			std::unique_ptr<rectangle>	new_rect( int x, int y, int width, int height, int depth_index = -1);
			std::unique_ptr<color>		new_color( float r, float g, float b );

die Member col und rect der Klasse brauchst du nicht mehr und auch im Destruktor musst du nichts freigeben.

Diese Methoden sehen dann implementiert so aus:
C++:
std::unique_ptr<rectangle> loop::new_rect( int x, int y, int width, int height, int depth_index)
{
	return std::unique_ptr<rectangle>(new rectangle(x, y, width, height, depth_index));

}

std::unique_ptr<color> loop::new_color( float r, float g, float b )
{
	return std::unique_ptr<color>(new color(r, g, b));
}

Ich habe den Klassen color und rectangle entsprechende Konstruktoren hinzugefügt damit es bisschen einfacher geht.

Und entsprechend ändert sich dann auch draw_scene:
C++:
	for(int i=0; i<10; i++)
	{

		for(int j=0; j<10; j++)
		{
			std::unique_ptr<color> colorPtr;
			if(map[i][j] == 1)
			{
				colorPtr = new_color( 1.0f, 0.0f, 0.0f );
			}else{
				colorPtr = new_color( 1.0f - 0.025f * i, 1.0f - 0.025f * j, 1.0f - 0.025f * j - 0.025f * i );
			}

			auto rectPtr = new_rect( i * 32 , j * 32, 32, 32, -5 );
			g.fill_rectangle(rectPtr, colorPtr );
				
			g.draw_rectangle( rectPtr, new_color( 1.0f, 0.0f, 0.0f ) );



		}

	}

Damit das ganze auch sauber mit der graphics-Klasse läuft werden auch deren Parameter angepasst:
C++:
		void						draw_rectangle( const std::unique_ptr<rectangle>& rect, const std::unique_ptr<color>& col, float width = 1 );
		void						draw_ellipse( const std::unique_ptr<rectangle>& rect, const std::unique_ptr<color>& col, float width = 1 );

		void						fill_rectangle( const std::unique_ptr<rectangle>& rect, const std::unique_ptr<color>& col);
		void						fill_ellipse( const std::unique_ptr<rectangle>& rect, const std::unique_ptr<color>& col);

entsprechend auch in der Implementation, das lass ich aber weg, du musst da ja nur die Parameterliste anpassen.

Für deine Anwendung war jetzt unique_ptr noch das richtige für new_color und new_rect, allerdings gibt es auch noch andere Varianten wie weak_ptr oder shared_ptr. Du musst je nach Einsatzgebiet schauen welches für dich der richtige ist, sie stechen alle jedoch immer die raw-Pointer aus in C++.

Grüsse
Cromon
 
Ok, Danke! Doch wiso ist es nicht mehr üblich die zeiger aus C zu verwenden? Kann ich in der der header datei auch so deklarieren:

C++:
           std::unique_ptr<color> colorPtr;
           std::unique_ptr<rectangle> rectPtr;
 
Zuletzt bearbeitet von einem Moderator:
Weil dir Zeiger nur Nachteile bringen ohne einen Vorteil zu haben. Von Speicherlecks über verwaiste Speicherzellen bis hin zu mehrfachem Löschen. Alles Dinge die du dank Templates und den Mechanics von C++ nicht mehr ertragen musst.

Ja, du kannst das definieren:
C++:
typedef std::unique_ptr<color> colorPtr;

Meistens wirst du jedoch mit shared_ptr arbeiten, das entspricht eigentlich eher so dem gängigen Zeiger da er den Besitz mit anderen teilt und entsprechend mitzählt wie viele andere auf den Speicher verweisen und wenn niemand mehr das tut automatisch den Speicherplatz frei gibt. Ein unique_ptr ist der einzige Besitzer des Speichers - wird er zerstört (destruktor) wird auch der Speicher freigegeben.
 
Naja, Zeiger rund heraus zu verteufeln finde ich auch nicht so die feine Englische-Art. Ich denke man sollte schon wissen, wie man mit ihnen umgeht, wenn man in C++ entwickelt. Immerhin kann es auch Plattformen geben, wo die Standard-Libraries vielleicht garnicht verfügbar sind. Dann hat man eventuell nur die C++ Erweiterungen des Compilers.

Aber es stimmt, mit den STL kann man sich das Leben schon vereinfachen.

Gruß,
Wolf
 
Die C++-Art ist eigentlich RAII, also wenig Zeiger, und die auch nicht in irgendwelche Pointer-Container zu stopfen.

Wenn möglich, Objekte by value weiterreichen, also ein color-Objekt direkt. Das kann man auch problemlos aus einer Funktion rausreichen.
 
Die C++-Art ist eigentlich RAII, also wenig Zeiger, und die auch nicht in irgendwelche Pointer-Container zu stopfen.

RAII und Zeiger stehen in keinem direkten Zusammenhang. Mit new dynamisch allozierte Objekte rufen ihren Konstruktor auf, RAII ist also genau gleich erfüllt wie ohne new. Ohne Pointer-Container hingegen ist es nicht wirklich erfüllt, da du das Objekt explizit mit einem delete-Aufruf freigeben musst, Pointer-Container das hingegeben automatisch übernehmen.

Wenn möglich, Objekte by value weiterreichen, also ein color-Objekt direkt. Das kann man auch problemlos aus einer Funktion rausreichen.

by value ist durchaus schön und möglich allerdings erfahrungsgemäss enden die meisten Programme mit falschen Copy-Konstruktoren, was zu mehrfach gelöschten Resourcen und so weiter führt.

@Markus Gögele:
Nein, durch Zeiger sparst du nicht wirklich nennenswert Speicher.
 
Also, die Weitergabe by Value halte ich in der Regel für weniger Ratsam. Für ganz kleine Objekte schön und gut, aber gerade wenn man mit größeren Datenstrukturen arbeitet - zum Beispiel in Bild-Bearbeitungs-Anwendungen, sollte man doch eher Zeiger und Referenzen auf Objekte verwenden um den Kopier-Aufwand zu verringern. Das kann schnell zu Performance-Einbußen führen.

Und Pointer sind ja auch nichts böses. Java zum Beispiel funktioniert die Weitergabe von Objekten an Funktionen ausschliesslich über Pointer.

Gruß,
Wolf
 
Zurück