正确删除std::列表中分配给其他地方的指针

Correctly delete pointers in std::list allocated elsewhere

本文关键字:其他 指针 删除 std 列表 分配      更新时间:2023-10-16

可能重复:
std::list::remove方法是否调用每个被移除元素的析构函数?

我有一个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(

如何正确地遍历它们,删除每一个

如果您想要安全和隐式资源清理,请不要使用原始指针,使用smart-pointers

STL容器的问题是:
如果包含的对象是指针,STL容器请勿拥有销毁它的所有权。您必须在每个包含的指针上显式调用delete来删除它所指向的内容。

看看这个类似的问题这里。

最好的方法不是将原始指针存储在STL容器中,而是使用它们的智能表亲智能指针(boost::shared_ptr(查看Boost文档,这些指针表亲足够智能,可以在没有人引用它们时自行解除分配,并为您省去现在面临的问题。

这段代码有很多错误。但它们都源于这条线:

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 s。