C++ Speicher wieder freigeben

Xervos

Erfahrenes Mitglied
Hallo Leute,

da ich ja gerade beim einlernen bin was c++ angeht bin ich gerade auf das Thema Pointer und adresse gestoßen wie soll es anders sein, verstehe ich das natürlich nicht gleich auf anhieb :).

So meine Aufgabenstellung nun. Ich möchte gerne in ein Window buttons zeichnen. Jetzt habe ich mir eine Klasse gebastelt die einen button abbildet.

Code:
class button
{
public:
	button(const std::String &sButtonImageFile,
			float fXposition,
			float fYposition,
			float fLength,
			float fHeight,
			GameState::GameState gsDrawOnGameState);
	
    button();	
	~button();
	
	// Functions for Set the Button Visible/Invisible this flag is stronger then bAvailability
	void SetButtonVisible(); // This function shoule be Called to set the Drawbutton flag Visible
	void SetButtonInVisible(); // This function shoule be Called to set the Drawbutton flag to InVisble 
	bool Visibility(); // This Function Returns if the Button should be Drawn or not
	
	// This Function Just say if the Aktiv/NoAktiv state should be performed
	void SetButtonEnable(); // This Function should be called to Enable the Button 
	void SetButtonDisable(); // This Function should be called to Disable the Button 
	bool Availability(); // This Function Returns if the Button is ClickAble or not
	
	//This Functions should be called to Set the Button Aktiv or not
	void SetButtonAktiv(); //will set the Button to Aktiv 
	void SetButtonInaktiv(); //will set the Button to InAktiv
	bool Activity();
	
	// Set SubRect for the Button and his two States
	void SetInAktivRectAngle(sf::IntRect irInAktivRect); // this Function should be Called to set SubRect to show when Button is not Aktiv -- NO Mouse over
	void SetAktivRectAngle(sf::IntRect irAktivRect); // this Function should be Called to set SubRect to show when Button is Aktiv -- When Mouse over
	
	//Set the GameState when the Button Is Fired
	void SetGameStateWhenButtonFired(GameState::GameState &gsButtonFired); // this Function should be called the State which will be Set when Button is Fired
	
	//ButtonFired Function
	GameState::GameState ButtonFired(); // This Function returns the GameState which should be set when button is fired
	
	// When should the Button be Loaded
	GameState::GameState& GetShowOnGameState();
	
	// Get The Sprite
	sf::Sprite& GetSprite();
	
private:
	
	sf::image _iLoadImage;
	sf::Sprite _spButtonSprite;
	
	sf::IntRect _irAktivRect;
	sf::IntRect _irInAktivRect;
	
	bool _bVisibility;
	bool _bAvailability;
	bool _bActivity;
	
	float _fLength;
	float _fHeight;
	
	GameState::GameState _gsLoadOnGameState;
	
	

};

so jetzt jetzt lege ich einen Button mit new an und möchte den dann auf eine Liste draufpacken.

diese habe ich so angelegt

Code:
std::list<Button> ButtonList;

So jetzt erzeuge ich meinen Button mit dem überladenen Konstruktor:

Code:
Button *_Button = new Button(i_szButtonImageFile, fXposition, fYposition, fLength, fHeight,gsDrawOnGameState);

so die ganzen Variablen bekomme ich selber schon übergeben. Ok wurde erzeugt und jetzt will ich das ganze auf die Liste Packen, natürlich nur die Adresse dazu und keine Kopie.

Code:
ButtonList.push_front(_Button);

so geht das aber nicht, was mache ich falsch ? Habe ich dir Liste überhaupt richtig angelegt oder ist das auch falsch ****

gehört die Liste und die Übergabe vll so ****

Code:
std::list<Button*> ButtonList;

Code:
ButtonList.push_front((*_Button));


Ich will ja nur die Speicheradresse haben um damit dann über einen Pointer auf die Klasse zugreifen zu können.

Kann mir da einer Helfen wie das den genau jetzt geht ****

Bitte bin schon am verzweifeln
 
Ich lerne c++ gerade selber erst, aber da du schon am verzweifeln bist will ich mich mal an dem Problem versuchen.
Zu aller erst einmal, würde ich vermuten, dass der Compiler meckert, was genau meckert er denn? Was gibt er aus? Das kann mitunter wirklich hilfreich sein wenn man versucht den Fehler zu finden.
Sollte der Compiler nicht meckern, sonder einfach nur dein Programm an dieser Stelle immer abstürzen, dann wäre die Ausgabe beim Absturz interessant.

Meine erste Idee wäre einfach nur die Liste anders anzulegen. Also einfach
Code:
std::list<*Button> ButtonList;

zu verwenden. Da du ja Pointer abspeichern willst wäre es ja auch logisch das dort direkt anzugeben.

Code:
ButtonList.push_front((*_Button));
Den Ansatz würde ich sofort vergessen, weil es einfach nicht das ist was du willst. Sei _Button ein Pointer aufeinen BUtton, so erhälst du mit *_Button wieder die Daten des Objekts. Willst du einen Pointer auf ein bereits existierendes Objekt so musst du ein & vor das Objekt machen.

Ich hoffe das hilft dir ein wenig weiter. Weil ich wie besagt selber noch nicht wirklich erfahren in c++ bin.
 
Hallo Danke dir mal für den Input.

meinst du das so ungefähr ?

Das ist die Liste:

Code:
std::list<*Button> ButtonList;

Kure frage macht es eigentlich einen Unterschied ob ich den * vor oder nach Button setzte ?

So jetzt möchte ich das ganze erzeugen und ablegen:

Code:
Button *_Button = new Button(i_szButtonImageFile);

	ButtonList.push_front(&_Button);

hmm das geht aber nicht wirklich:

Code:
error C2662: 'std::list<_Ty,_Ax>::push_front': this-Zeiger kann nicht von 'std::list' in 'std::list<_Ty,_Ax> &' konvertiert werden


EDIT:

SO geht es übringends:

LISTE:

Code:
std::list<Button*> ButtonList;

übergabe

Code:
Button *_Button = new Button(i_szButtonImageFile);

	ButtonList.push_front(_Button);

nur die Frage ob ich da jetzt wieder nur eine Copy übergebe oder wirklich die Adresse
 
Zuletzt bearbeitet:
Hallo Xervos,

Das Template das du bei der Liste angibst sagt ihr, welche Typen von Objekten sie empfangen kann.
std::list<Button> -> Elemente müssen als Button auffassbar sein
std::list<Button*> -> Elemente müssen als Button* auffassbar sein
std::list<*Button> -> Keine gültige Syntax, da *Button kein Typ ist
std::list<Button**> -> Elemente müssen als Button** auffassbar sein

Wenn du also Button* _Button = new ...; hast, dann sind deine Elemente vom Typ Button*. Ergo muss die Liste Elemente vom Typ Button* fassen können, wenn du sie direkt übergibst. Das wäre dann std::list<Button*>. Dann kannst du ihr einfach via eine der Insert-Funktionen den _Button übergeben. Theoretisch kannst du auch std::list<Button> verwenden und an die Insert-Funktion den dereferenzierten _Button (mit ButtonList.push_front(*_Button)) übergeben, aber das wäre schlecht, weil du dann (sofern du _Button nirgendwo anders speicherst) den Speicher nicht mehr freigeben kannst.

/Edit:
Zum freigeben. Wenn du in der List die Adressen der Buttons speicherst (Button*), dann kannst du über alle Elemente iterieren und delete auf die Adressen aufrufen, dann ist der Speicher wieder freigegeben.

Gruss
Muepe
 
Hallo danke dir mal für die Antwort,

ich habe es jetzt so gelöst wie oben beschrieben. Also ich hoffe ich speicher jetzt nur die Adressen und erzeuge keine Objekte ^^

also wenn ich das jetzt löschen will so zb.

Code:
for(std::list<Button*>::iterator it = ButtonList.begin(); it != ButtonList.end(); ++it)
	{

		
	    Button *_delButton((*it));
		it = ButtonList.erase(it);
		delete _delButton;


	}

dann sprint er in den Dekonstruktor von Button, aber bekomme ich eine Fehler Meldung

Code:
0xC0000005: Zugriffsverletzung beim Lesen an Position 0x00000000

hmm also nachdem er wieder vom dekonstruktor zurück kommen will macht er das oder kann ich direkt auf den Iterator zugreifen ?

zb so

Code:
	for(std::list<Button*>::iterator it = ButtonList.begin(); it != ButtonList.end(); ++it)
	{

		it = ButtonList.erase(it);
		delete (*it);


	}

an dem freigeben hagt es noch ein bisschen muss ich sagen wie kann ich das lösen hat da wer ein Codebsp vll?
 
Hi

nur die Frage ob ich da jetzt wieder nur eine Copy übergebe oder wirklich die Adresse
Wenn du es so machst wie in deinem edit, also beim Anlegen Stern danach und beim übergeben ohne Stern, passt das so.
Sollte funktionieren und ist wirklich nur die Adresse, keine Kopie.

Und zum Iterieren: Ja, man muss sogar. Zugriff über Index geht bei einer std::list nicht.
Warum verwendest du überhaupt eine, mit nur einem Button?
Auch wenn es mehrere werden, sind es wohl kaum beliebig viele; Buttons müssen ja auch irgendwo auf den Bildschirm.

Wieder entfernen kann man ein mit new angelegtes Objekt jedenfalls mit delete.
Einfach so:
C++:
Button *but;
but=new Button(...);
delete but;

Wenn man seine Objekte nicht wieder deletet, bleibt das dem Betriebssystem überlassen.
Je nachdem, wie gut es seine Aufräumarbeiten macht (oder ob es überhaupt welche macht), müllst du dir mit delete-losem Code auf kurz oder lang den RAM zu.
Folge: Alles wird Langsamer, bis zum Steckenbleiben vom gesamten Computer.
Mit Aus/Einschalten ist dann wieder alles in Ordnung.

Gruß

PS: Waa, 2 Beiträge zu spät :(
 
Hi.
ich habe es jetzt so gelöst wie oben beschrieben. Also ich hoffe ich speicher jetzt nur die Adressen und erzeuge keine Objekte ^^

also wenn ich das jetzt löschen will so zb.

Code:
for(std::list<Button*>::iterator it = ButtonList.begin(); it != ButtonList.end(); ++it)
	{

		
	    Button *_delButton((*it));
		it = ButtonList.erase(it);
		delete _delButton;
}

dann sprint er in den Dekonstruktor von Button, aber bekomme ich eine Fehler Meldung

Code:
0xC0000005: Zugriffsverletzung beim Lesen an Position 0x00000000
Du darfst nicht noch ++it aufrufen, wenn du den Iterator schon auf das nächste Element durch list<T>::erase gesetzt hast.

Bsp:

Liste.size() == 1:

it = Liste.begin() // <- it zeigt auf erstes Element
it = Liste.erase(it) // <- it zeigt auf List.end()
++it // it ist ungültig. it != List.end()
hmm also nachdem er wieder vom dekonstruktor zurück kommen will macht er das
Dein Iterator zeigt auf kein valides Objekt.
oder kann ich direkt auf den Iterator zugreifen ?
Natürlich.
zb so

Code:
	for(std::list<Button*>::iterator it = ButtonList.begin(); it != ButtonList.end(); ++it)
	{

		it = ButtonList.erase(it);
		delete (*it);
}
Nein, so nicht. Du mußt zuerst das Objekt freigeben und danach den Eintrag aus der Liste entfernen:
C++:
delete(*it);
it = ButtonList.erase(it);
Und auch in der Schleife darfst du nicht ++it aufrufen.

Außerdem wäre es günstiger, wenn du sowieso alle Objekte aus der Liste löschen möchtest, dass du nicht erase() in der Schleife aufrufst sondern erst nach der Schleife clear().

Gruß
 
Hallo Danke dir mal für die Antwort.


Hmm also ich verstehe das gerade nicht sooo ganz, wie meinst du das ich darf in der Schleife nicht mit ++it zugreifen ?
 
Hmm ich glaube ich stelle das ganze auf einen Vector um.

Warum verwendest du überhaupt eine, mit nur einem Button?
Auch wenn es mehrere werden, sind es wohl kaum beliebig viele; Buttons müssen ja auch irgendwo auf den Bildschirm.

Also es sollen in Zukunft schon mehrere Geben auch auf anderen Views Sprich im MainWindow gibt es welche dann wenn ich da Credits auswähle gibt es welche usw. Ich möchte diese ganze liste schon mehr befüllen als nur mit einem Button :).

Ich habe das ganze jetzt mal umgestellt auf ein einen Vector soll performanter sein wie ich gelesen habe. Die Löschung funkt auch ohne das ich einen Speicherzugriffsverletzung bekomme.

Hoffe ich lösche mit meiner methode wirklich alles, also den Pointer und das Objekt selber

Code:
std::vector<Button*> _vButtonList;

danach kann ich so das ganze in mein Window laden

Code:
	for(unsigned int i = 0; i < _vButtonList.size(); i++)
	{
		
		m_Window->Draw((*_vButtonList[i]).GetSprite());
	}


und es so wieder aus dem Vector löschen

Code:
	for(unsigned int i = 0; i < _vButtonList.size(); i++)
	{
		delete _vButtonList[i];
	}

	_vButtonList.clear();

Frage hierzu. Lösch ich dann wirklich alles oder ? den Pointer und das Objekt selber ?
 
Zuletzt bearbeitet:
Zurück