0

SFML2 スプライト用の単純なスプライト キャッシュを作成しています。スプライトへのポインターのマップを保持するマネージャー クラスがあります。また、所有者マップへの参照を保持するスプライト クラスもあります。問題はスプライトのデストラクタにあります。次のようになります。

~ActualSprite()
{
    if(m_iteratorLocation != m_ownerMap.end())
    {
        m_ownerMap.erase(m_iteratorLocation);
    }
}

m_iteratorLocation は、スプライト マップ内のスプライトの現在の位置であると想定されています。スプライト コンストラクターで初期化されます。ここでは、スプライト マネージャーからのスプライト作成メソッドを示します。

SpritePtr getSprite(SpriteId name)
    {
        if(!spriteMap[name])
        {
            spriteMap[name] = std::tr1::make_shared< ActualSprite >(spriteMap, spriteMap.find(name));
            clipSprite(name);
            return spriteMap[name];
        }
        else
            return spriteMap[name];

    }

基本的に、プログラムを終了すると、次のような例外/エラー メッセージが表示されますExpression: map/set iterator outside range

spriteMap.find(name)最初は、名前が見つからず、spriteMap.end()代わりに返されるためにこれが起こると思っていました。しかし、私はそれを理解していません。キーをマップにspriteMap[name]追加するという最初の言及ではありませんか? nameとにかく、イテレータが.end()と等しくない場合にのみマップエントリを消去するifステートメントを追加しましたが、それでもポップアップします。

基本的に、イテレータの代わりに、消去に enum という名前を使用して動作しますが、エラー メッセージが表示される理由を知りたいと思っています。

これは、現在の作業バージョンと、エラーをスローするコメント付きイテレータ バージョンを含む完全なコードです。

#include <SFML/Graphics.hpp>
#include <memory>
#include <map>


enum SpriteId
{
    ITEM1,
    ITEM2,
    ITEM3,
    ITEM4,
    ITEM5
};

const int WIDTH = 100;
const int HEIGHT = 100;

class ActualSprite;

typedef std::tr1::shared_ptr< ActualSprite > SpritePtr;
typedef std::map< SpriteId, SpritePtr > SpriteMap;

class ActualSprite : public sf::Sprite
{
private: 
    //SpriteMap::iterator m_iteratorLocation;
    SpriteMap &m_ownerMap;
    SpriteId &m_name;
public:
    //ActualSprite(SpriteMap &ownerMap, SpriteMap::iterator iteratorLocation) : m_ownerMap(ownerMap), m_iteratorLocation(iteratorLocation)
    //{}

    ActualSprite(SpriteMap &ownerMap, SpriteId &name) : m_ownerMap(ownerMap), m_name(name)
    {}

    ~ActualSprite()
    {
        m_ownerMap.erase(m_name);
    }

    //~ActualSprite()
    //{
    //  if(m_iteratorLocation != m_ownerMap.end())
    //  {
    //      m_ownerMap.erase(m_iteratorLocation);
    //  }
    //}
};

class SpriteManager
{
private:
    SpriteMap spriteMap;
    sf::Texture& m_texture;
    void clipSprite(SpriteId name)
    {
        spriteMap.at(name)->setTexture(m_texture);
        switch(name)
        {
        case ITEM1: spriteMap.at(name)->setTextureRect(sf::IntRect(0,0,WIDTH,HEIGHT));break;
        case ITEM2: spriteMap.at(name)->setTextureRect(sf::IntRect((1*WIDTH),0,WIDTH,HEIGHT));break;
        case ITEM3: spriteMap.at(name)->setTextureRect(sf::IntRect((2*WIDTH),0,WIDTH,HEIGHT));break;
        case ITEM4: spriteMap.at(name)->setTextureRect(sf::IntRect((3*WIDTH),0,WIDTH,HEIGHT));break;
        case ITEM5: spriteMap.at(name)->setTextureRect(sf::IntRect((4*WIDTH),0,WIDTH,HEIGHT));break;
        //default: exception or somethin'
        }
    }
public:
    SpriteManager(sf::Texture& texture) : m_texture(texture)
    {}
    SpritePtr getSprite(SpriteId name)
    {
        if(!spriteMap[name])
        {
            spriteMap[name] = std::tr1::make_shared< ActualSprite >(spriteMap, name);
            /*spriteMap[name] = std::tr1::make_shared< ActualSprite >(spriteMap, spriteMap.find(name));*/
            clipSprite(name);
            return spriteMap[name];
        }
        else
            return spriteMap[name];

    }
};

int main()
{
    sf::RenderWindow window(sf::VideoMode(800,600), "Test", sf::Style::Titlebar | sf::Style::Close);

    sf::RectangleShape background(sf::Vector2f(800.0f,600.0f));

    window.setFramerateLimit(30);

    sf::Texture spriteSheet;

    if(!spriteSheet.loadFromFile("SpriteSheet.png"))
    {
        return 1;
    }

    SpriteManager sprites(spriteSheet);

    SpritePtr sprite = sprites.getSprite(ITEM2);
    SpritePtr sprite2 = sprites.getSprite(ITEM4);

    sprite->setPosition(100,100);
    sprite2->setPosition(200,100);

    while(window.isOpen())
    {
        sf::Event event;
        while( window.pollEvent(event))
        {
            if(sf::Mouse::isButtonPressed(sf::Mouse::Left))
            {
                sf::Vector2i currentPos = sf::Mouse::getPosition(window);
                sprite->setPosition((static_cast<float>(currentPos.x) - (WIDTH/2)), (static_cast<float>(currentPos.y) - (HEIGHT/2)));
            }
            if(event.type == sf::Event::Closed)
            {
                window.close();
            }
        }

        window.clear();
        window.draw(background);
        window.draw(*sprite);
        window.draw(*sprite2);
        window.display();
    }

    return 0;
}

注:これは単なるテストであるため、すべてが 1 つの .cpp ファイルにあり、項目名が説明的ではないのはそのためです。

4

3 に答える 3

3

SpriteManager問題は、オブジェクトに埋め込まれたマップのデストラクタが、それらを削除するためにすべての要素を反復処理するという事実に最も可能性があります。保存された共有ポインターの削除/破棄時に、これらがオブジェクトへの最後の共有ポインターである場合、あなたのデストラクタSpriteが呼び出されます。

次に、これは格納されたイテレータを使用して、対応する要素をマップから削除しようとします。ただし、この反復子が指す要素は、マップのデストラクタのサイクルから削除されたため、無効化されています。最終的にイテレータを引数として渡すと、erase()未定義の動作が発生します(あなたの場合、幸いにもクラッシュとして現れます)。

もちろん、マップから共有ポインターを削除しても、あなたのデストラクタが直接Sprite呼び出されない場合でも、問題は発生します (それを指す他の共有ポインターがあるため): その場合、実際には、マップ内の要素は既に消去されている必要があり、無効化されたイテレータが残ります。のデストラクタSpriteが呼び出されると、無効な反復子が に渡されerase()、未定義の動作が再び発生します。

于 2013-02-10T16:59:43.337 に答える
1

shared_ptr のマップは、オブジェクトを消去するときにオブジェクトのデストラクタを呼び出します。

あなたの場合、含まれているオブジェクトのデストラクタはオブジェクトを消去します。

これを行うと、あらゆる種類の奇妙な問題に遭遇することになります。weak_ptr を使用しても問題は解決しません。せいぜい隠すかもしれません。

また、このコードはあなたが思っていることをしません:

if(!spriteMap[name])
    {

std::mapオブジェクトが存在しない場合、コンテナーはそのキーを使用してオブジェクトを作成します。存在をテストしたい場合に使用したいのは、 ですfind

幸運にも、あなたが含んでいるオブジェクトのいくつかの素晴らしい結果に陥ったのは、たまたまです。bool 演算子を使用すると、shared_ptr はゼロで初期化され、null の shared_ptr は false とテストされます。

于 2013-02-13T18:36:11.087 に答える
0

問題はSpriteMap、 を含む が、そこに含まれるオブジェクトshared_ptr<Sprite>の (共有) 所有者であることです。Spriteしたがって、 aSpriteは、含まれている から削除された後にのみ破棄できますSpriteMap。その時点Spriteで、マップ内で this を指していたイテレータは無効になりました。

あなたのコードでは、これは@Andy Prowl で説明されているようSpriteManagerに、最後に が破棄されたときに発生します。他のときmain()に s を破棄する操作があった場合も、同じことが起こります。Sprite

Spriteでライフタイムを管理したい場合は、デストラクタSpriteManagerに自己登録解除コードは必要ありません。寿命を の外での使用のみに依存さSpriteせたい場合は、保留にすることができます。その場合、その自己登録解除コードを保持するか、次のアクセス試行までマップに期限切れのポインターを残すことができます。SpriteSpriteMapSpriteMapweak_ptr<Sprite>

于 2013-02-10T18:05:31.197 に答える