2

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

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

4

2 回答 2

8

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

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

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

解决这个问题的最好方法不是在 STL 容器中存储原始指针,而是使用它们的智能表兄弟智能指针(boost::shared_ptr)查看Boost 文档,这些指针表兄弟足够智能,可以在没有人引用它们时释放自己并保存你的问题就像你现在面临的问题。

于 2011-07-17T06:01:07.693 回答
1

这段代码有很多问题。但它们都源于这一行:

std::list<Sprite*>* _sprite = NULL;

除非您使用 C++0x,否则它不会编译。您不能像这样设置非静态成员的值。除非你打算让它是静态的,如果你这样做了,你应该使用static关键字。

但更糟糕的是您在std::list堆上分配 a 。为什么?您在需要时分配一个,并在析构函数中释放它。只需将其设为常规成员变量即可。

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。这是因为您现在无法将 a 传递Sprite*std::list::remove我们正在使用智能指针。如果这样做,它将把它包装在一个boost::shared_ptr临时文件中,从而导致指针被删除。这很糟糕,所以我不得不使用自定义测试仪。

此外,如果您想将Sprite对象注册到一个类中,那么Sprite对象构造函数(或工厂方法)应该进行此注册。用户不应该能够创建未注册Sprite的 s。

于 2011-07-17T06:25:32.000 回答