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

时间:2011-07-17 05:55:37

标签: c++ memory-management iterator stdlist

  

可能重复:
  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)。

如何正确迭代它们,删除每一个?

2 个答案:

答案 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