Map.esers()抛出一个错误,原因是什么
Map.erase() throws an error, why?
我一直在为SFML2精灵制作一个简单的精灵缓存。我有一个管理器类,它包含指向精灵的指针映射。我还有一个sprite类,它引用了它的所有者映射。现在问题出在精灵的析构函数中。它看起来像这样:
~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文件中,以及为什么项目名称不是描述性的。
问题很可能是因为嵌入在SpriteManager
对象中的映射的析构函数遍历其所有元素以删除它们。删除/销毁存储的共享指针后,如果这些是指向对象的最后一个共享指针,则会调用Sprite
的析构函数。
反过来,这将尝试使用存储的迭代器从映射中删除相应的元素。然而,这个迭代器所指向的元素刚刚从映射的析构函数中的循环中删除,因此无效。将迭代器作为参数传递给erase()
最终会导致Undefined Behavior(幸运的是,在您的情况下,它表现为崩溃)。
当然,即使从映射中删除共享指针并没有直接导致Sprite
的析构函数被调用(因为还有其他指向它的共享指针),这个问题也会出现:在这种情况下,事实上,映射中的元素必须已经被擦除了,剩下的是一个无效的迭代器。当调用Sprite
的析构函数时,它将向erase()
传递一个无效的迭代器,从而再次导致Undefined Behavior。
shared_ptr的映射在擦除对象时将调用对象的析构函数。
在您的案例中,所包含对象的析构函数会擦除该对象。
你这样做会遇到各种奇怪的问题。使用weak_ptr也不能解决问题。可能会把它藏起来。
此外,这个代码并没有做你认为它会做的事情:
if(!spriteMap[name])
{
如果该键不存在,std::map
容器将创建具有该键的对象。如果你想测试存在性,你想使用的是find
。
碰巧的是,你很幸运地发现了你所包含的对象的一些好结果。shared_ptr将被零初始化,而使用bool运算符时,null shared_ptr测试为false。
问题是包含shared_ptr<Sprite>
的SpriteMap
是它所包含的Sprite
对象的(共享)所有者。因此,Sprite
只能在从包含的SpriteMap
中移除之后被销毁。此时,用于在映射中指向此Sprite
的迭代器已变为无效。
在您的代码中,当SpriteManager
在main()
结束时被销毁时,就会发生这种情况,正如@Andy Prowl所解释的那样。如果在任何其他时间都有处理Sprite
的操作,也会发生同样的情况。
如果希望SpriteManager
管理Sprite
生存期,则不需要Sprite
析构函数中的自注销代码。如果您希望Sprite
的生存期仅取决于SpriteMap
之外的使用,则可以使SpriteMap
保持weak_ptr<Sprite>
。在这种情况下,您可以保留自注销代码,或者将过期的指针留在映射中,直到下一次尝试访问为止。
- C++,我收到一个无法理解的编译器错误
- VSCode-有一个红色下划线,但程序构建和运行正确,并且出现配音错误
- 删除映射和分割错误中的一个过去结束元素
- 为什么我的 heap.h 文件给我一个LNK2001错误?
- 写一个分数类,我无法弄清楚这些错误
- 我在 .h 中有一个枚举类,并且在.cpp错误中有一个运算符重载:与"运算符<<不匹配
- 一个标头库中的错误
- 给定一个整数数组,需要在Max_Heap上运行操作。得到错误"segmentation fault",有什么想法吗?(C++)
- 使用Visual Studio在虚幻引擎中创建一个新的类c ++给了我太多的错误
- 我正在尝试制作一个自平衡机器人,但编译时存在错误。我不知道如何解决它
- 试图修复一个错误,该错误不会让我开始编程其余部分
- MSVC 无法根据模板参数进行数学运算,这是一个错误吗?
- 我正在尝试一个傻瓜 C++ 练习,我遇到了一个错误,说类 'GraduateStudent' 没有任何名为 'advisor' 的字段
- 零四元数和任何向量都不为零的特征积,这是一个错误吗?
- 我有一个线程 1:EXC_BAD_ACCESS(代码 = 1,地址 = 0x8)错误.我认为这是由于内存管理不好.我可以
- 两个线程一个使用流 Api,另一个线程创建文件失败并出现错误ERROR_SHARING_VIOLATION
- 在使用堆栈为下一个最大数字编写代码时面临 SIGSEGV(分段错误)
- LNK2019错误一个DLL链接到MFC DLL
- 错误:一个声明中有多个类型
- 带有两个运算符的C++11模糊错误(一个左值-第二个右值).底部的