C++ Löschen von Objekten aus Vektor.

Bronislaw

Cadet 2nd Year
Registriert
Juli 2014
Beiträge
28
Hallo Leute, sitze schon seit Tagen daran und finde keine Lösung für folgendes Problem. (Visual Studio 10 C++ mit SFML 2.1)
In Anlehnung an den alten Beitrag zu einem ähnlichen Fall, der nunmehr weit über 2 Jahre zuürckliegt, (vgl.
HTML:
https://www.computerbase.de/forum/threads/objekte-aus-vector-loeschen-waehrend-der-iterator-duch-den-vector-geht.957663/
, habe ich versucht den dortigen Lösungsansatz aus Beitrag#6 (remove_if als Struct-Variante) umzusetzen.
Bisher leider ohne Erfolg. Hier ist der Ausgangscode, wo der Iterator nach Löschen des letzten Sprites den Fehler "Vektor Iterator nicht inkrementierbar" erzeugt.


Jetzt wollte ich fragen, wie man das richtig umsetzt. Es geht lediglich um den Erase-Befehl und diese remove_if oder andere unbekannte Alternativen, der übrige Code ist fürs Verständnis da und zur freien Verwendung.

Code:
#include <SFML/Graphics.hpp>
#include <iostream>
#include <sstream>
#include <vector>
#include <algorithm>

int main()
{

    sf::RenderWindow mMainWindow(sf::VideoMode(1000, 600), "Map");
    mMainWindow.setFramerateLimit(30);
   
    sf::Image unitimage;
    unitimage.loadFromFile("unit.png");
    sf::Texture unittexture;
    unittexture.loadFromImage(unitimage);
    sf::Sprite unitsprite(unittexture);

    std::vector<sf::Sprite>EnemyVector;
    std::vector<sf::Sprite>::iterator EnemyIt;

while (mMainWindow.isOpen())
    {
        sf::Event event;
        while (mMainWindow.pollEvent(event))
            {
            if (event.type == sf::Event::Closed)
                {
                mMainWindow.close();
                }
            if(event.type == sf::Event::MouseButtonPressed)
                if(event.mouseButton.button == sf::Mouse::Left)
                    {
                    sf::Sprite *Sprite;
                    Sprite = new sf::Sprite;
                    Sprite->setTexture(unittexture);
                    Sprite->setPosition(sf::Mouse::getPosition(mMainWindow).x,sf::Mouse::getPosition(mMainWindow).y);

                    EnemyVector.push_back(*Sprite);
                    std::cout << "Objects count: " << EnemyVector.size() << std::endl;
                    }          
            }

    for(EnemyIt = EnemyVector.begin();EnemyIt != EnemyVector.end();EnemyIt++)
        {
        mMainWindow.draw(*EnemyIt);
            if(event.mouseButton.button == sf::Mouse::Right)
                {
                sf::Vector2f mousecoords(mMainWindow.mapPixelToCoords(sf::Vector2i(event.mouseButton.x, event.mouseButton.y)));
                if(EnemyIt->getGlobalBounds().contains(mousecoords))
                    {                                                                                      

                    EnemyVector.erase(EnemyIt);
                    EnemyIt = EnemyVector.begin();


                    }
                }
        }
        mMainWindow.display();
		mMainWindow.clear(sf::Color(0, 200, 0, 255));

    }
    return 0;
}



Sorry für den vielen Code. Das ist der neue Code:
Jetzt hat er mir bei der Struct nach und nach alles als Fehler markiert, wenn ich z.B. das e oder current_hp entfernen wollte. Wie kann man den Code richtig machen, dass er keinen Fehler mehr anzeigt. Ich weiß, diese Struct ist falsch, aber weiß nicht was. In den Büchern/Dokumentation wird das alles nur allgemein beschrieben und hilft hier überhaupt nicht weiter. Ich möchte einfach die letze Einheit löschen können bzw. in der unteren Variante das Programm überhaupt zum laufen zu bekommen :-) Bitte helft mir.

Code:
#include <SFML/Graphics.hpp>
#include <iostream>
#include <sstream>
#include <vector>
#include <algorithm>

struct IsEnemyDead {
	bool operator() (const Sprite& e) const {
		return e current_hp <= 0;
	}
};


int main()
{

    sf::RenderWindow mMainWindow(sf::VideoMode(1000, 600), "Map");
    mMainWindow.setFramerateLimit(30);
   
    sf::Image unitimage;
    unitimage.loadFromFile("unit.png");
    sf::Texture unittexture;
    unittexture.loadFromImage(unitimage);
    sf::Sprite unitsprite(unittexture);

    std::vector<sf::Sprite>EnemyVector;
    std::vector<sf::Sprite>::iterator EnemyIt;

while (mMainWindow.isOpen())
    {
        sf::Event event;
        while (mMainWindow.pollEvent(event))
            {
            if (event.type == sf::Event::Closed)
                {
                mMainWindow.close();
                }
            if(event.type == sf::Event::MouseButtonPressed)
                if(event.mouseButton.button == sf::Mouse::Left)
                    {
                    sf::Sprite *Sprite;
                    Sprite = new sf::Sprite;
                    Sprite->setTexture(unittexture);
                    Sprite->setPosition(sf::Mouse::getPosition(mMainWindow).x,sf::Mouse::getPosition(mMainWindow).y);

                    EnemyVector.push_back(*Sprite);
                    std::cout << "Objects count: " << EnemyVector.size() << std::endl;
                    }          
            }

    for(EnemyIt = EnemyVector.begin();EnemyIt != EnemyVector.end();EnemyIt++)
        {
        mMainWindow.draw(*EnemyIt);
            if(event.mouseButton.button == sf::Mouse::Right)
                {
                sf::Vector2f mousecoords(mMainWindow.mapPixelToCoords(sf::Vector2i(event.mouseButton.x, event.mouseButton.y)));
                if(EnemyIt->getGlobalBounds().contains(mousecoords))
                    {

                    EnemyVector.erase(
						std::remove_if(
						EnemyVector.begin(), EnemyVector.end(),
						IsEnemyDead()),
						EnemyVector.end()
						);

                    EnemyIt = EnemyVector.begin();


                    }
                }
        }
        mMainWindow.display();
		mMainWindow.clear(sf::Color(0, 200, 0, 255));

    }
    return 0;
}
 
Zuletzt bearbeitet:
remove_if löscht elemente aus dem Array/Vector raus.
als return wert liefert es das element nach dem letzten gelöscht raus (beim schluss ist es vector::end())

Code:
 EnemyVector.erase(
std::remove_if(
EnemyVector.begin(), EnemyVector.end(),
IsEnemyDead()),
EnemyVector.end()
);

hier löscht du also die Elemente so wie gewünscht
und danach nochmals das Element nach dem gelöschten, was natürlich nicht immer klappt, bzw. ich glaub nicht das es das ist was du willst.

ich würde hier einfach händisch rüber iterieren, dann die gewünschten einträge rauslöschen (über erase)
also zuerst den Speicher freigeben (delete iterator_pos) und dann erst das erase machen..
 
Der Code ist etwas irritierend: Du benennst die Variable einer Instanz mit dem Namen der Klasse:
sf::Sprite Sprite. Das würde ich mir mal gleich abgewöhnen.

Im Struct hast du als Typ Sprite angegeben, der müsste wohl sf::Sprite heißen.

Außerdem willst du remove_if das Fuktionsobjekt, nicht das Ergebnis einer Funktion liefern, also darfst du das Funktionsobjekt nicht aufrufen:
Code:
std::remove_if(EnemyVector.begin(), EnemyVector.end(), IsEnemyDead())
ändern zu:
Code:
std::remove_if(EnemyVector.begin(), EnemyVector.end(), IsEnemyDead)
Aufrufen wird schließlich remove_if das Objekt, und zwar für jedes Element das im angegeben Bereich liegt.
Nach weiteren Fehlern hab ich jetzt nicht geschaut.

Von irgendwelchen händischen rauslöschoperationen wie im Beitrag vor mir würde ich drigend abraten.
In C++11, also der neueren Fassung geht das ganze etwas einfacher per Lambda, statt umständlich ein Funktionsobject zu verwenden:
Code:
std::remove_if(EnemyVector.begin(), EnemyVector.end(), [](const sf::Sprite& e) {
//hier jetzt der Rumpf einer Funktion die bool zurückgibt
});
Als Rumpf hab ich jetzt mal nicht den aus dem struct übernommen da "return e current_hp <= 0;" kein gültiger Code ist.

Edit: Wie gesagt, remove_if prüft alle Elemente im angegebenen Bereich, als Bereich gibst du alle Elemente an, aber über die iterierst du ja bereits. Das ganze ergibt mal überhaupt gar keinen Sinn. Entweder iterierst du über die Elemente oder du verwendet remove_if.
Achja und gleich als Hinweis: Wenn der Vektor seine Speicherkapazität ändert aufgrund geänderter Anzahl der Elemente die er enthält werden Iteratoren ungültig (die zeigen weiterhin auf die alte Speicherstelle), also fröhlich weiteriterieren nach erase wird katastrophale Folgen haben. Mit der ersten Codevariante fängst du immer wieder von vorne an und außerdem wird X mal der Vektor in der Größe geändert, was beides ungünstiges Laufzeitverhalten erzeugt. Besser irgendein Variante finden, bei der du nur einmal remove_if aufrust, ganz ohne irgendwelches händischen iterieren.
 
Zuletzt bearbeitet:
Noch einen Fehler gefunden
Code:
 sf::Sprite *Sprite;
Sprite = new sf::Sprite;
Sprite->setTexture(unittexture);
Sprite->setPosition(sf::Mouse::getPosition(mMainWindow).x,sf::Mouse::getPosition(mMainWindow).y);
 
EnemyVector.push_back(*Sprite);

du legst ein Sprite mit new an, dieses kopierst du aber "per Value" in den EnemyVector...
danach löscht due das sprite aber nie mehr..
d.h. du hast da ein schönes Speicherloch.

Entweder du hast einen Vector von pointern, wo du den speicher selber verwaltest (delete/new)
oder du machst das alles perValue/reference der Vector verwaltet den Speicher.
Du mischt das in deinem code, das geht so nicht (bzw. da musst auf vieles aufpassen, das es sinnvoll mischen geht)
 
Vielen Dank für die bisherigen Tipps.
Er markiert mir zu viele Fehler, wenn ich an einer anderen Stelle etwas ändern möchte. Wenn ich mal so überlege, sollte ich es vllt. doch über eine Liste probieren, weil die nur ein Anfang und Ende hat. Da gibts dann wenigstens auch nicht diese Struct/Klasse :-) und ist alles fast dasselbe. Allerdings tu ich mich hier mit dem Memory leak schwer. An welcher Stelle sollte man das Sprite am besten löschen? In der Liste hab ich jetzt auch so ein Lambda. Außerdem alles nochmal etwas "verschlankt". Soll man auch hier hinter das "Lambda" eine bool Fkt. machen?

Code:
#include <SFML/Graphics.hpp>
#include <iostream>
#include <list>

int main()
{

    sf::RenderWindow mMainWindow(sf::VideoMode(1000, 600), "Map");
    mMainWindow.setFramerateLimit(30);
   
    sf::Texture unittexture;
    unittexture.loadFromFile("unit.png");

    std::list<sf::Sprite> EnemyList;			//Erstelle eine Liste vom Spritetyp und dem Namen "EnemyList"
    std::list<sf::Sprite>::iterator EnemyIt;		// Erstelle einen Listiterator vom Spirtetyp mit Namen "EnemyIt"

while (mMainWindow.isOpen())
    {
        sf::Event event;
        while (mMainWindow.pollEvent(event))
            {
            if (event.type == sf::Event::Closed)
                {
                mMainWindow.close();
                }
            if(event.type == sf::Event::MouseButtonPressed)
                if(event.mouseButton.button == sf::Mouse::Left)
                    {
					
                    sf::Sprite *Sprite;			// Erstelle einen Zeiger mit dem Namen Sprite, der auf Spritetyp-Daten zeigt.
                    Sprite = new sf::Sprite;		// Fordere während Laufzeit Speicher für eine Variable vom Typ Sprite an.
					Sprite->setTexture(unittexture);  // Zeigerzugriff auf ein Klassenmember
                    Sprite->setPosition(sf::Mouse::getPosition(mMainWindow).x,sf::Mouse::getPosition(mMainWindow).y); // Zeigerzugriff auf ein Klassenmember

                    EnemyList.push_back(*Sprite);   // Füge neues Element an Listenende an.
                    std::cout << "Objects count: " << EnemyList.size() << std::endl;  // Gebe die Elementenzahl aus.
                    }          
            }

    for(EnemyIt = EnemyList.begin();EnemyIt != EnemyList.end();EnemyIt++) //For-Schleife mit Iterator: Setze Iterator auf den Anfang und laufe Schleife durch, solange das Ende nicht erreicht ist und inkrementiere dabei den Iterator
        {
        mMainWindow.draw(*EnemyIt);  // Zeichne im Fenster den Variablenwert, auf den der Zeiger zeigt.
            if(event.mouseButton.button == sf::Mouse::Right)
                {
                sf::Vector2f mousecoords(mMainWindow.mapPixelToCoords(sf::Vector2i(event.mouseButton.x, event.mouseButton.y)));
                   

                    EnemyList.remove_if([=](sf::Sprite unitsprite){return unitsprite.getGlobalBounds().contains(mousecoords); });
                }
        }
        mMainWindow.display();
        mMainWindow.clear(sf::Color(0, 200, 0, 255));

    }
    return 0;
}
 
Zuletzt bearbeitet:
Warum tust du dich mit dem Löschen schwer? Es gibt nur zwei Zeilen, an denen du das Objekt sinnvoll löschen kannst. 35/36 oder 36/37.
 
So?

Code:
         EnemyList.push_back(*Sprite);
		    delete Sprite;
                    std::cout << "Objects count: " << EnemyList.size() << std::endl;
		    std::cout << "Objects count: " << Sprite << std::endl;


Was mach ich jetzt aber gegen die Access Violation beim Löschen der zuerst erstellten Einheit? Interessant ist auch die Ausgabe, wo die erste Einheit eine andere Adresse hat als die meisten anderen.


Oder statt den beiden
Code:
         sf::Sprite *Sprite;			
                    Sprite = new sf::Sprite;

sowas einfügen?
Code:
		std::unique_ptr<sf::Sprite> Sprite(new sf::Sprite);
 
Zuletzt bearbeitet: (Smart Pointer)
Die Adressen sind doch vollkommen uninteressant. Sie sind quasi random und erklären dir nichts. Den smart-Pointer kannst du gern verwenden, wenn du ihn verstehst. Ansonsten tut's derweil auch new und delete.
Der Fehler ist aber, dass das remove[_if]() die Iteratoren und Zeiger auf den Vektor invalidiert. Mit anderen Worten: nach einem Aufruf von std::remove, remove_if oder auch std::vector::erase sind eventuell sämtliche Iteratoren ungültig. Zieh' das remove samt zugehöriger if-Bedingung einfach aus der Schleife.
 
Ok, vielen Dank für die Hilfe. Jetzt geht alles ohne Absturz. Alles korrigiert, indem ich noch ein Spezialrechteckerstellt habe :-)

Code:
      #include <SFML/Graphics.hpp>
    #include <iostream>
    #include <list>
     
    int main()
    {
     
	sf::RectangleShape rectangle;
	rectangle.setSize(sf::Vector2f(1000, 600));
	rectangle.setFillColor(sf::Color(100,80,100,100));
	rectangle.setPosition(0, 0);

	sf::FloatRect rightBound = rectangle.getGlobalBounds();



    sf::RenderWindow mMainWindow(sf::VideoMode(1000, 600), "Map");
    mMainWindow.setFramerateLimit(60);
    sf::Texture unittexture;
    unittexture.loadFromFile("warrior.png");
     





    std::list<sf::Sprite> EnemyList;
    std::list<sf::Sprite>::iterator EnemyIt;
     
    while (mMainWindow.isOpen())
    {
    sf::Event event;
    while (mMainWindow.pollEvent(event))
    {
    if (event.type == sf::Event::Closed)
	{
    mMainWindow.close();
    }
	if(event.type == sf::Event::MouseButtonPressed)
		if(event.mouseButton.button == sf::Mouse::Right)
	{
	sf::Vector2f mousecoords(mMainWindow.mapPixelToCoords(sf::Vector2i(event.mouseButton.x, event.mouseButton.y)));
		EnemyList.remove_if([=](sf::Sprite unitsprite){return unitsprite.getGlobalBounds().contains(mousecoords); });
	}
    if(event.type == sf::Event::MouseButtonPressed)
		if(event.mouseButton.button == sf::Mouse::Left)
			if (rectangle.getGlobalBounds().contains(mMainWindow.mapPixelToCoords(sf::Mouse::getPosition(mMainWindow))))		
		{
		sf::Sprite *Sprite;
		Sprite = new sf::Sprite;
		Sprite->setTexture(unittexture);
		Sprite->setPosition(sf::Mouse::getPosition(mMainWindow).x,sf::Mouse::getPosition(mMainWindow).y);
		EnemyList.push_back(*Sprite);
		delete Sprite;
		
		}
	}
    for(EnemyIt = EnemyList.begin();EnemyIt != EnemyList.end();EnemyIt++)
		{
		mMainWindow.draw(*EnemyIt);

		}
		mMainWindow.display();
		mMainWindow.clear(sf::Color(0, 200, 0, 255));
    }

    return 0;
    }
 
Zuletzt bearbeitet: (Richtige Version)
Der Beitrag von the_nobs hat es schon mal angesprochen ... du erstelltst mit new in Zeile 50 eine neue Sprite-Instanz auf dem Free Store, steckst dann anschließend eine Kopie dieser Instanz in deinen vector und löscht anchließend das Original mit delete wieder ab.

Wozu erstellst du die Sprite-Instanz also überhaupt auf dem Free Store? Lege sie einfach normal auf dem Stack an; das ist

a) schneller (Free Store Allozierungen und Deallozierungen sind relativ zeitaufwändig, da es sich um eine System-Ressource handelt)
b) simpler (weder new noch delete werden benötigt)
c) sinnvoller, da der einzige Vorteil, den der Free Store bietet (die Lebensdauer eines Objekts nicht an den umgebenden Gültigkeitsbereich zu binden sondern selbst bestimmen zu können) von dir hier gar nicht benötigt wird.

Code:
{
    sf::Sprite newSprite;
    newSprite.setTexture(unittexture);
    newSprite.setPosition(sf::Mouse::getPosition(mMainWindow).x,sf::Mouse::getPosition(mMainWindow).y);
    EnemyList.push_back(newSprite);
}

Ich habe in dem Beispiel der Instanz auch den Namen newSprite statt Sprite gegeben, da es generell unschön ist, Variablennamen mit Großbuchstaben zu beginnen und dann auch noch einen Name zu verwenden, der exakt der gleiche ist wie der Name der Klasse (minus dem vorangehenden Namespace-Präfix).
 
Zuletzt bearbeitet:
Das ist richtig genial :-)
Besten Dank für die präzise Erklärung.

Jetzt hab ich nochmal ne Frage. Es geht ums Auswählen einzelner Sprites (z.B. auch für den Zweck diese zu löschen, um beim Thema zu bleiben). Ist es möglich auf die Sprites zu klicken und wenn sich im "Klickbereich" mehrere Sprites befinden, dann immer automatisch das, welches oben auf liegt, auszuwählen und bspw. dieses dann zu löschen. Welcher der Listenbefehle käme dafür in Frage? Oder braucht man dafür ne neue for Schleife, die bei mehreren mit dem "Klickbereich" kollidierenden Listenelementen automatisch das in der Liste Letzte (=Sprite liegt oben auf) davon auswählt.
 
Zuletzt bearbeitet:
Muss nochmal stören, aber mir lässt der Vektor einfach keine Ruhe :-) (wie auch das Thema dieses Strangs lautet)
Liste kann ich machen, aber Vektor scheint unmöglich zu sein.
Hab aus dem Internet und den entsrp. Referenzen alle Lösungsmöglichkeiten durchprobiert; ohne Erfolg. Lambda geht wegen VS 2010 auch nicht.

Habe versucht jetzt die obigen Tipps umzusetzen und auch den Code zu verfeinern, aber er zeigt mir beim Kompilieren einen Fehler. Es geht um das "IsEnemyDead" in der Zeile mit
Code:
std::remove_if(SpriteVector.begin(), SpriteVector.end(), IsEnemyDead);
Er sagt in Bezug auf dieses "IsEnemyDead", dass dieser Ausdruck in ungültiger Weise als Typ verwendet wird und ich auch in Zeile 6 wegen der Deklaration schauen soll (da wo die Struct mit Namen versehen wird). Das Erstellen einer bool hat hier auch nicht geholfen. Weiß keinen Rat mehr. Vielen Dank im Voraus für jede Hilfe dazu!

Ich möchte einfach nur die Einheiten einzeln löschen; beim Draufklicken :-)


Das ist der gesamte Code:

Code:
#include <SFML/Graphics.hpp>
#include <iostream>
#include <vector>
#include <algorithm>

struct IsEnemyDead {
bool operator() (const sf::Sprite& e) const {
return 0;
}
};


int main()
{
    sf::RenderWindow mMainWindow(sf::VideoMode(1200, 900), "Map");
    mMainWindow.setFramerateLimit(60);

	sf::RectangleShape rect;
    rect.setSize(sf::Vector2f(1500, 500));
    sf::Texture unittexture;
    unittexture.loadFromFile("warrior.png");

    std::vector<sf::Sprite> SpriteVector;
	std::vector<sf::Sprite>::iterator SpriteIt;

	//bool IsEnemyDead = false;

    while (mMainWindow.isOpen())
    {
        sf::Event event;
        while (mMainWindow.pollEvent(event))
        {
            if (event.type == sf::Event::Closed)
			{
                mMainWindow.close();
			}
			else if (event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::A)   
			{
				sf::Sprite newSprite;
				newSprite.setTexture(unittexture);
				newSprite.setPosition(mMainWindow.mapPixelToCoords(sf::Vector2i(sf::Mouse::getPosition(mMainWindow).x-50,sf::Mouse::getPosition(mMainWindow).y-50))); 
				SpriteVector.push_back(newSprite);
			}
			else if (event.type == sf::Event::MouseButtonPressed && event.mouseButton.button == sf::Mouse::Right)
				for(SpriteIt = SpriteVector.begin();SpriteIt != SpriteVector.end();SpriteIt++)
					{
						sf::Vector2f mousecoords(mMainWindow.mapPixelToCoords(sf::Vector2i(event.mouseButton.x, event.mouseButton.y)));
						if(SpriteIt->getGlobalBounds().contains(mousecoords))
						{
							 std::remove_if(SpriteVector.begin(), SpriteVector.end(), IsEnemyDead);
						}
					}
        }	

    mMainWindow.clear();
	mMainWindow.draw(rect);

        for(auto &SpriteIt = SpriteVector.begin();SpriteIt != SpriteVector.end();++SpriteIt)
		{
			mMainWindow.draw(*SpriteIt);
		}

        mMainWindow.display();
		

    }

    return 0;
}
 
Die Funktion, die du remove_if() übergibtst, darf keine nicht-statische Member-Funktion sein. Mach sie entweder static oder nimm sie aus dem struct raus und mach eine freie Funktion daraus.


EDIT: Schwachsinn, Gehirnfurz .... bitte ignorieren, während ich noch mal kurz in mich gehe. :o

Zweiter Edit: Du gibst bei remove_if einen Typ an (IsEnemyDead). Die Funktion möchte aber ein Funktor-Objekt. Also:

std::remove_if(SpriteVector.begin(), SpriteVector.end(), IsEnemyDead());


P.S. Nicht vergessen, daß remove_if() allein eigentlich gar nix removt. In deinem aktuellen Beispiel steht das remove_if allein, ohne erase().
 
Zuletzt bearbeitet:
Das erase voran setzen? Das hab ich probiert und der Iterator ist dann außer Reichweite (outside range).
Hab einige versch. Dinge gerade aus dem Netz probiert, aber keine der Lösungen davon geht (spätestens beim Klicken fällt das dann auf).


Code:
SpriteVector.erase(std::remove_if(SpriteVector.begin(), SpriteVector.end(), IsEnemyDead()));
 
Wenn du den rot markierten Teil noch hinzufügst, muß es gehen.

Code:
SpriteVector.erase(std::remove_if(SpriteVector.begin(), SpriteVector.end(), IsEnemyDead())[B][COLOR="#FF0000"],SpriteVector.end()[/COLOR][/B]);

Ohne den rot markierten Teil entfernt erase() nicht einen Bereich sondern nur ein einzelnes Element.

EDIT: Grrrr, innerhalb eines code-Tags kann man anscheinend keine Farben nutzen (eigentlich logisch, wenn ich darüber nachdenke :rolleyes:). Dann eben ohne code-Tags:

SpriteVector.erase(std::remove_if(SpriteVector.begin(), SpriteVector.end(), IsEnemyDead()),SpriteVector.end());
 
Das hab ich auch schon (mehrmals :-) ) probiert und es geht leider nicht: (d.h. zwar kein Fehler, aber es tut sich nix)

Code:
SpriteVector.erase(std::remove_if(SpriteVector.begin(), SpriteVector.end(), IsEnemyDead()),SpriteVector.end());
 
Im Zweifelsfall immer die Referenz zu Rate ziehen:


iterator erase (const_iterator position);
iterator erase (const_iterator first, const_iterator last);


Erase elements

Removes from the vector either a single element (position) or a range of elements ([first,last)).

http://www.cplusplus.com/reference/vector/vector/erase/
Ergänzung ()

Bronislaw schrieb:
Das hab ich auch schon (mehrmals :-) ) probiert und es geht leider nicht: (d.h. zwar kein Fehler, aber es tut sich nix)

Code:
SpriteVector.erase(std::remove_if(SpriteVector.begin(), SpriteVector.end(), IsEnemyDead()),SpriteVector.end());

Du meinst, es wird nix entfernt? Dann muß es daran liegen, daß dein IsEnemyDead-Funktor nicht die Wahrheit sagt. Debugge mal in den Funktor-Aufruf rein und überprüfe, ob das Ding wirklich korrekt erkennt, ob die Feinde tot sind.
 
Referenz hab ich auch geschaut und z.B. folgender Code löscht alles so wie ich will, aber bei der letzten Sprite zerstört sich der Iterator selbst:

Code:
							 SpriteVector.erase(SpriteIt);
							 SpriteIt = SpriteVector.begin();
 
Bronislaw schrieb:
Referenz hab ich auch geschaut und z.B. folgender Code löscht alles so wie ich will, aber bei der letzten Sprite zerstört sich der Iterator selbst:

Code:
							 SpriteVector.erase(SpriteIt);
							 SpriteIt = SpriteVector.begin();

Was heißt "der Iterator zerstört sich selbst" (nicht bös' gemeint, ich verstehe die Aussage so echt nicht)? Kannst du vielleicht noch mal deinen jetzigen (möglichst vollständigen) Code posten?
 
Damit meine ich "vector iterator not incrementable". Der erste, oberste Block voller Code demonstriert dies, wenn man diesen eins zu eins ins programm reinkopiert.
Und der aktuelle Code, der keinen Fehler anzeigt, aber dennoch nicht geht, ist der Folgende:

Code:
#include <SFML/Graphics.hpp>
#include <iostream>
#include <vector>
#include <algorithm>

struct IsEnemyDead {
bool operator() (const sf::Sprite& e) const {
return 0;
}
};

int main()
{
    sf::RenderWindow mMainWindow(sf::VideoMode(1200, 900), "Map");
    mMainWindow.setFramerateLimit(60);

	sf::RectangleShape rect;
    rect.setSize(sf::Vector2f(1500, 500));
    sf::Texture unittexture;
    unittexture.loadFromFile("warrior.png");

    std::vector<sf::Sprite> SpriteVector;
	std::vector<sf::Sprite>::iterator SpriteIt;

    while (mMainWindow.isOpen())
    {
        sf::Event event;
        while (mMainWindow.pollEvent(event))
        {
            if (event.type == sf::Event::Closed)
			{
                mMainWindow.close();
			}
			else if (event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::A)   
			{
				sf::Sprite newSprite;
				newSprite.setTexture(unittexture);
				newSprite.setPosition(mMainWindow.mapPixelToCoords(sf::Vector2i(sf::Mouse::getPosition(mMainWindow).x-50,sf::Mouse::getPosition(mMainWindow).y-50))); 
				SpriteVector.push_back(newSprite);
			}
			else if (event.type == sf::Event::MouseButtonPressed && event.mouseButton.button == sf::Mouse::Right)
				for(SpriteIt = SpriteVector.begin();SpriteIt != SpriteVector.end();SpriteIt++)
					{
						sf::Vector2f mousecoords(mMainWindow.mapPixelToCoords(sf::Vector2i(event.mouseButton.x, event.mouseButton.y)));
						if(SpriteIt->getGlobalBounds().contains(mousecoords))
						{
							SpriteVector.erase(std::remove_if(SpriteVector.begin(), SpriteVector.end(), IsEnemyDead()),SpriteVector.end());
						}
					}
        }	

    mMainWindow.clear();
	mMainWindow.draw(rect);

        for(auto &SpriteIt = SpriteVector.begin();SpriteIt != SpriteVector.end();++SpriteIt)
		{
			mMainWindow.draw(*SpriteIt);
		}

        mMainWindow.display();
		

    }

    return 0;
}
 
Zurück
Oben