可能重复:
Does std::list::remove method call destructor of each removed element?
我有一个SpriteHandler类,允许用户注册一个指向Sprite对象的指针以进行绘图,它所做的只是对象的访问方法。我想编写一个安全捕获程序,如果用户在程序结束时忘记这样做,就会自动删除与指针关联的内存(并且不用担心用户也是这样!):
//SpriteHandler.h
class SpriteHandler {
public:
//...
void RegisterObject(Sprite* object);
bool IsRegistered(Sprite* object);
void UnregisterObject(Sprite* object);
private:
//...
static std::list<Sprite*>* _sprite = NULL;
};
//SpriteHandler.cpp
std::list<Sprite*>* SpriteHandler::_sprites = NULL;
void SpriteHandler::RegisterObject(Sprite* object) {
if(object == NULL) return;
if(_sprites == NULL) _sprites = new std::list<Sprite*>();
_sprites->push_back(object);
_sprites->sort(UDLessSprite);
}
bool SpriteHandler::IsRegistered(Sprite* object) {
return std::binary_search(_sprites->begin(), _sprites->end(), object);
}
void SpriteHandler::UnregisterObject(Sprite* object) {
if(object == NULL) return;
if(IsRegistered(object) == false) return;
_sprites->remove(object);
if(_sprites->size() <= 0) {
if(_sprites) {
_sprites->clear();
delete _sprites;
_sprites = NULL;
}
return;
}
_sprites->sort(UDLessSprite);
}
void SpriteHandler::Release() {
if(_sprites) {
std::list<Sprite*>::iterator _iter = _sprites->begin();
while(_iter != _sprites->end()) {
delete (*_iter);
(*_iter) = NULL;
++_iter;
}
_sprites->clear();
delete _sprites;
_sprites = NULL;
}
}
我遇到的问题是在第一个指针被删除后,下一个迭代器指向一个已经释放的对象(内存位置为0xfeeefeee)。
如何正确迭代它们,删除每一个?
答案 0 :(得分:7)
如果您想要安全和隐式资源清理,请不要使用原始指针,使用智能指针!
STL容器的问题是:
如果包含的对象是指针STL容器不要取得破坏它的所有权。
您必须在每个包含的指针上显式调用delete以删除它指向的内容。
看看这个类似的问题 here 。
解决这个问题的最好方法是不将原始指针存储在STL容器中,而是使用智能指针代替智能指针(boost::shared_ptr
)查看Boost documentation,这些指针表兄弟足够聪明,可以解除分配当没有人提到他们时,他们自己就能解决你现在面临的问题。
答案 1 :(得分:1)
这段代码有很多问题。但他们都来自这条线:
std::list<Sprite*>* _sprite = NULL;
除非您使用的是C ++ 0x,否则无法编译。您不能像这样设置非静态成员的值。除非您打算将其设为静态,否则您应该使用static
关键字。
但更糟糕的是你在堆上分配std::list
。为什么?您在需要时分配一个,并在析构函数中释放它。只需将其设为常规成员变量即可。
C ++ 不是 Java。并非一切都必须成为指针。
如果您要声明对这些Sprite
个对象的所有权,则需要实际声明对它们的所有权。这最好用某种智能指针完成。至少应该有std::list<std::auto_ptr<Sprite> >
这样可以确保在从列表中删除条目时删除Sprite
个对象。如果您有权访问Boost(如果您没有,则需要访问它),您可以使用boost::shared_ptr
。 C ++ 0x与std::shared_ptr
提供的内容大致相同。
std::auto_ptr
只允许一个对象拥有指针,而boost::shared_ptr
允许共享所有权(因此名称)。这对您的代码来说不是必需的(至少我们可以看到),但允许Sprite
个对象的共享所有权并不是一个坏主意。在C ++ 0x中,您应该使用std::unique_ptr
而不是std::auto_ptr
。
无论哪种方式,您的代码现在都是这样的:
//SpriteHandler.h
class SpriteHandler {
public:
//...
void RegisterObject(Sprite* object);
bool IsRegistered(Sprite* object);
void UnregisterObject(Sprite* object);
private:
//...
std::list<boost::shared_ptr<Sprite> > _sprite;
};
void SpriteHandler::RegisterObject(Sprite* object) {
if(!object) return;
_sprites.push_back(object);
_sprites.sort(UDLessSprite);
}
bool SpriteHandler::IsRegistered(Sprite* object) {
return std::binary_search(_sprites.begin(), _sprites.end(), object);
}
struct SpriteTester{
SpriteTester(Sprite *testValue) : _testValue(testValue) {}
bool operator()(const boost::shared_ptr<Sprite> &other) const{
return other.get() == _testValue;
}
Sprite *_testValue;
};
void SpriteHandler::UnregisterObject(Sprite* object) {
if(object == NULL) return;
_sprites.remove_if(object, SpriteTester(object));
//Deleting an entry cannot make the list unsorted.
}
void SpriteHandler::Release() {
_sprites.clear();
}
请注意我介绍了SpriteTexture。这是因为我们现在正在使用智能指针,因此您无法将Sprite*
传递给std::list::remove
。如果这样做,它会将其包装在boost::shared_ptr
临时值中,从而导致指针被删除。这很糟糕,所以我不得不使用自定义测试仪。
此外,如果要在类中注册Sprite
个对象,则Sprite
对象构造函数(或工厂方法)应该进行此注册。用户不应该能够创建未注册的Sprite
。