2

重複の可能性:
std::list::remove メソッドは、削除された各要素のデストラクタを呼び出しますか?

ユーザーが描画用の Sprite オブジェクトへのポインターを登録できるようにする SpriteHandler クラスがあります。オブジェクトのメソッドにアクセスするだけです。ユーザーがプログラムの最後までに削除するのを忘れた場合に、ポインタに関連付けられたメモリを自動的に削除する安全キャッチを書きたかった (そして、ユーザーにとっても心配する必要はありません!):

 

//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> >確実に削除されます。SpriteBoost にアクセスできる場合 (ない場合は、アクセスする必要があります) を使用できますboost::shared_ptr。C++0x は とほぼ同じものを提供しますstd::shared_ptr

std::auto_ptr1 つのオブジェクトのみがポインターを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*to を渡すことができないためです。std::list::removeその場合、boost::shared_ptr一時的にラップされるため、ポインターが削除されます。これはまずいので、カスタム テスターを使用する必要がありました。

Spriteまた、オブジェクトをクラスに登録したい場合は、Spriteオブジェクト コンストラクター (またはファクトリ メソッド) がこの登録を行う必要があります。ユーザーは未登録の を作成できませんSprite

于 2011-07-17T06:25:32.000 に答える