2011-07-17 4 views
2

可能性の重複:
Does std::list::remove method call destructor of each removed element?は正しく他の場所に割り当てられたのstd ::リスト内のポインタを削除

は、私は、ユーザーが描画用の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; 
    } 
} 

私は "mが持つ問題は、最初のポインタが削除された後、次のイテレータは(メモリ位置が0xfeeefeeeです)、すでに解放されたオブジェクトを指していることである。

私が正しく、それぞれ1?

を削除、それらを反復処理する方法を
+1

スマートポインタを使用してください! :) – GManNickG

+1

'std :: binary_search'を呼び出すと、リンクリストの' O(log(n)) 'パフォーマンスが得られません。' O(log(n)) 'の比較が行われます。リストはランダムアクセスイテレータをサポートしていないので、O(n)オペレーションをすべて実行します。おそらくあなたはこれを既に知っているかもしれませんが、もしあなたがそうしないと言ったら... :) –

+0

ブーストを使用している場合は、ブーストポインタコンテナ(boost :: ptr_listなど)を使用することもできます。それらは指し示されたオブジェクトの所有権を取り、それらを自動的に削除します。 (http://www.boost.org/doc/libs/1_47_0/libs/ptr_container/doc/ptr_container.html) – Ferruccio

答えて

7

安全性と暗黙的なリソースクリーンアップが必要な場合は、 生ポインタスマートポインタを使用しないでください!

STLコンテナの問題点は次のとおりです。
含まれるオブジェクトは、それを破壊する所有権の取得をしていないポインタSTLコンテナである場合。 ポインタが指す内容を削除するには、含まれている各ポインタでdeleteを明示的に呼び出さなければなりません。

hereをご覧ください。

STLコンテナの中に未処理のポインタを格納するのではなく、インテリジェントないとこのスマートポインタを使用してください(boost::shared_ptrBoost documentationをチェックしてください。ポインタの同類は、あなたが今直面しているような問題をあなたに救います。

+0

1.35よりも新しいバージョンのBoostのドキュメントにリンクしたい場合があります。 Theatは7リリースと数年前のようでした。 –

+0

@ニコル・ボラス:ありがとうございました!私はリンクを更新しました。 –

+2

スマートポインタは、最近ほとんどのコンパイラで実装/サポートされているC++標準ライブラリTR1で利用可能ですが、ブーストスマートポインタの使用をアドバイスしている人も多くいます。ブースト、TR1とC++ 0xとの比較については、http://www.codesynthesis.com/~boris/blog/2010/05/24/smart-pointers-in-boost-tr1-cxx-x0/を参照してください。 http://www.codeguru.com/cpp/cpp/cpp_mfc/stl/article.php/c15361 TR1スマートポインターの機能について素敵な実践的な紹介があります(ピート・ベッカーの記事がたくさんあります例えばDDJが思い浮かびます)。 – celavek

1

このコードには多くの問題があります。しかし、それらはすべてこの行に由来します:

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

C++ 0xを使用していない限り、これはコンパイルされません。このような非静的メンバーの値は設定できません。これを静的にすることを意図していない場合は、staticキーワードを使用してください。

さらに悪いことに、std::listをヒープに割り当てているということです。どうして?必要なときに割り当て、デストラクタで割り当てを解除します。ちょうどそれを普通のメンバ変数にしてください。

C++はではなく、 Javaです。すべてがポインタでなければならないわけではありません。

これらのオブジェクトの所有権を主張している場合は、実際にオブジェクトの所有権を主張する必要があります。これは、ある種のスマートポインタで行うことが好ましいでしょう。少なくとも、std::list<std::auto_ptr<Sprite> >が必要です。これにより、リストからエントリを削除するときに、Spriteオブジェクトが確実に削除されます。 Boostへのアクセス権がある場合(アクセスしない場合は、アクセス権が必要です)、boost::shared_ptrを使用できます。C++ 0xはstd::shared_ptrとほとんど同じです。

std::auto_ptrは、1つのオブジェクトがポインタを所有できるようにし、boost::shared_ptrは共有所有(したがってその名前)を許可します。これはあなたのコードには必要ではありませんが(少なくとも見ることができます)、Spriteオブジェクトの共有所有を許可することは悪い考えではありません。 C++ 0xでは、std::auto_ptrの代わりにstd::unique_ptrを使用する必要があります。

いずれかの方法で、あなたのコードは次のようになります:私はSpriteTextureを導入

//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(); 
} 

注意してください。これは、スマートポインタを使用しているのでSprite*std::list::removeに渡すことができないためです。そうした場合、一時的にboost::shared_ptrにラップされ、ポインタが削除されます。これは悪いので、カスタムテスターを使用しなければなりませんでした。

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

+0

欠落した 'static'は、タイプミスです。あなたはその点を欠いている。ハンドラはすべてリストを反復処理し、Drawメソッドを呼び出してオブジェクト自体のバージョンに委譲するか、独自のバージョンを使用します。ユーザーは自由に特定のオブジェクトの描画をオンまたはオフにできるようにオブジェクトを登録および登録解除できます。私は何もクリーンアップをしていない*、それは単にユーザーの利便性です。 – Casey

+0

@Casey:ユーザーが自由にスプライトを自由に登録および登録解除できる場合、ユーザーが「リリース」を呼び出したときにスプライトを削除するのはなぜですか?これは所有権の移転を示唆していますが、登録解除機能によって削除されないという事実とは矛盾しています。レジストリは、ポインタを所有しているか、それらの所有権を共有しています(この場合、これを管理するためにスマートポインタが必要です)。または、それらの所有権がまったくありません。後者が本当であれば、どんな状況下でも決して削除してはなりません**。後者は、誰かが最初に登録を解除することなくポインタを削除した直後に失敗します。 –

+0

通常の条件では、ユーザーが50のスプライトを作成した場合、プログラムの最後に*を使用しないように手動ですべて削除する必要があります。SpriteHandler *は、このルートを開始する前に、所有権を取得せず、ポインタのコレクションを管理(追加、削除、および委任)しました。 'Release'はプログラムの最後でのみ呼び出されるように意図されています。もしユーザがプログラムの途中でハンドラを解放するほど愚かであれば、それは私のせいではなく、RTFMを持つべきです。 – Casey

関連する問題