Я делаю простой спрайт-кеш для спрайтов SFML2. У меня есть класс менеджера, который содержит карту указателей на спрайты. И у меня также есть класс спрайтов, который содержит ссылку на карту владельца. Теперь проблема заключается в деструкторе спрайта. Это выглядит следующим образом:Map.erase() выдает ошибку, почему?
~ActualSprite()
{
if(m_iteratorLocation != m_ownerMap.end())
{
m_ownerMap.erase(m_iteratorLocation);
}
}
m_iteratorLocation должен быть текущим местоположением спрайта в карте спрайтов. Он инициализируется в конструкторе спрайтов и здесь метод создания спрайтов из менеджера спрайтов
SpritePtr getSprite(SpriteId name)
{
if(!spriteMap[name])
{
spriteMap[name] = std::tr1::make_shared<ActualSprite>(spriteMap, spriteMap.find(name));
clipSprite(name);
return spriteMap[name];
}
else
return spriteMap[name];
}
В основном, когда я выйти из программы я получаю сообщение об отклонениях/об ошибке с указанием: Expression: map/set iterator outside range
.
Сначала я подумал, что это происходит потому, что spriteMap.find(name)
не может найти имя и возвращает spriteMap.end()
. Но я не понимаю, не первое упоминание о spriteMap[name]
добавляет ключ name
к карте? В любом случае я добавил оператор if только для удаления записи карты, если итератор не равен .end(), но он все же появляется.
В основном теперь вместо итератора я использую имя enum для стирания, и оно работает, но я все еще хочу знать, почему я получал сообщение об ошибке.
Это полный код с текущей рабочей версией и прокомментированная версия итератора, которая выдает ошибку.
#include <SFML/Graphics.hpp>
#include <memory>
#include <map>
enum SpriteId
{
ITEM1,
ITEM2,
ITEM3,
ITEM4,
ITEM5
};
const int WIDTH = 100;
const int HEIGHT = 100;
class ActualSprite;
typedef std::tr1::shared_ptr<ActualSprite> SpritePtr;
typedef std::map< SpriteId, SpritePtr > SpriteMap;
class ActualSprite : public sf::Sprite
{
private:
//SpriteMap::iterator m_iteratorLocation;
SpriteMap &m_ownerMap;
SpriteId &m_name;
public:
//ActualSprite(SpriteMap &ownerMap, SpriteMap::iterator iteratorLocation) : m_ownerMap(ownerMap), m_iteratorLocation(iteratorLocation)
//{}
ActualSprite(SpriteMap &ownerMap, SpriteId &name) : m_ownerMap(ownerMap), m_name(name)
{}
~ActualSprite()
{
m_ownerMap.erase(m_name);
}
//~ActualSprite()
//{
// if(m_iteratorLocation != m_ownerMap.end())
// {
// m_ownerMap.erase(m_iteratorLocation);
// }
//}
};
class SpriteManager
{
private:
SpriteMap spriteMap;
sf::Texture& m_texture;
void clipSprite(SpriteId name)
{
spriteMap.at(name)->setTexture(m_texture);
switch(name)
{
case ITEM1: spriteMap.at(name)->setTextureRect(sf::IntRect(0,0,WIDTH,HEIGHT));break;
case ITEM2: spriteMap.at(name)->setTextureRect(sf::IntRect((1*WIDTH),0,WIDTH,HEIGHT));break;
case ITEM3: spriteMap.at(name)->setTextureRect(sf::IntRect((2*WIDTH),0,WIDTH,HEIGHT));break;
case ITEM4: spriteMap.at(name)->setTextureRect(sf::IntRect((3*WIDTH),0,WIDTH,HEIGHT));break;
case ITEM5: spriteMap.at(name)->setTextureRect(sf::IntRect((4*WIDTH),0,WIDTH,HEIGHT));break;
//default: exception or somethin'
}
}
public:
SpriteManager(sf::Texture& texture) : m_texture(texture)
{}
SpritePtr getSprite(SpriteId name)
{
if(!spriteMap[name])
{
spriteMap[name] = std::tr1::make_shared<ActualSprite>(spriteMap, name);
/*spriteMap[name] = std::tr1::make_shared<ActualSprite>(spriteMap, spriteMap.find(name));*/
clipSprite(name);
return spriteMap[name];
}
else
return spriteMap[name];
}
};
int main()
{
sf::RenderWindow window(sf::VideoMode(800,600), "Test", sf::Style::Titlebar | sf::Style::Close);
sf::RectangleShape background(sf::Vector2f(800.0f,600.0f));
window.setFramerateLimit(30);
sf::Texture spriteSheet;
if(!spriteSheet.loadFromFile("SpriteSheet.png"))
{
return 1;
}
SpriteManager sprites(spriteSheet);
SpritePtr sprite = sprites.getSprite(ITEM2);
SpritePtr sprite2 = sprites.getSprite(ITEM4);
sprite->setPosition(100,100);
sprite2->setPosition(200,100);
while(window.isOpen())
{
sf::Event event;
while(window.pollEvent(event))
{
if(sf::Mouse::isButtonPressed(sf::Mouse::Left))
{
sf::Vector2i currentPos = sf::Mouse::getPosition(window);
sprite->setPosition((static_cast<float>(currentPos.x) - (WIDTH/2)), (static_cast<float>(currentPos.y) - (HEIGHT/2)));
}
if(event.type == sf::Event::Closed)
{
window.close();
}
}
window.clear();
window.draw(background);
window.draw(*sprite);
window.draw(*sprite2);
window.display();
}
return 0;
}
Примечание: Это просто тест, так что именно поэтому все в одном файле .cpp и почему имена элементов не являются описательными.
Я думаю, что это правильная причина, поскольку итераторы 'std :: map' не стираются при стирании. –
@AndreiTita: Не итераторы для элементов, кроме стираемых. Однако итераторы стираемых элементов недействительны. –
@ AndyProwl Я вижу, спасибо за ответ. «Передача итератора в качестве аргумента для стирания() в конечном итоге приводит к неопределенному поведению (которое в вашем случае удачно проявляется как сбой).« Это предложение относится только к моему делу или я должен воздерживаться от использования итераторов в качестве аргументов для «стирания»() 'вообще? Также делает ли мой '.erase (name)' фактически что-то сейчас? Судя по тому, что вы сказали, с '.erase()' в моем коде вообще не нужно? – MrPlow