【问题标题】:std::shared_ptr _BLOCK_TYPE_IS_VALID(pHead->nBlockUse) when changing pointer更改指针时的 std::shared_ptr _BLOCK_TYPE_IS_VALID(pHead->nBlockUse)
【发布时间】:2012-08-20 07:31:59
【问题描述】:

当这个函数运行时,我得到了sprite = spr; 行标题中描述的调试断言错误。如果我在此之前添加sprite.reset();,它会在sprite.reset(); 的行上崩溃。指针被存储在其他地方,在static std::map<std::string,sf::Sprite> ResourceManager::sprites;,所以我也不希望为 sf::Sprite 调用析构函数(尽管我怀疑这可能是因为它们被存储在静态对象中...... ?)

VisibleGameObject.h

#ifndef VISIBLEGAMEOBJECT_H
#define VISIBLEGAMEOBJECT_H

#include "Game.h"

//just for keeping track of the sprite and drawing

class VisibleGameObject{
public:
    VisibleGameObject(){}
    VisibleGameObject(const std::string& name);
    ~VisibleGameObject();

    std::string getTextureName();
    void setSprite(const std::string& textureName);
    void setSprite(const sf::Texture& texture);
    void setSprite(std::shared_ptr<sf::Sprite> sprite);

    void setPosition(float x,float y);
    void setPosition(const sf::Vector2f& position);

    void setRotationDegrees(float degrees);
    void setRotationRadians(float radians);
    float getRotationDegrees();
    float getRotationRadians();

    void setOrigin(float x,float y);
    void setOrigin(const sf::Vector2f& origin);

    sf::Vector2f getSize();
    sf::Vector2f getOrigin();
    sf::Vector2f getPosition();
    std::shared_ptr<sf::Sprite> getSprite();

    void draw(tgui::Window* wnd);
private:
    std::string name;
    std::shared_ptr<sf::Sprite> sprite;
    std::string texture_name;
    bool _loaded;
};

#endif

从 VisibleGameObject.cpp 中提取

//'sprite' is initialised here
    void VisibleGameObject::setSprite(const std::string& textureName){  
        sprite = std::shared_ptr<sf::Sprite>(ResourceManager::createSpriteFromStoredTexture(textureName,name));
        texture_name = textureName;
        _loaded = true;
    }


//error function!
void VisibleGameObject::setSprite(std::shared_ptr<sf::Sprite> spr){
    sf::Vector2f p(0,0);
    float d = 0;
    if(_loaded){
        p = spr->getPosition();
        d = spr->getRotation();
    }
    sprite = spr;
    sprite->setPosition(p);
    sprite->setRotation(d);
    _loaded = true;
}

从 ResourceManager.cpp 中提取

sf::Sprite* ResourceManager::createSpriteFromStoredTexture(const std::string& texturename,const std::string& spritename){
    sf::Sprite spt;
    spt.setTexture(*getTextureByName(texturename));
    std::string name = spritename;
    if(spritename == standard_spt_name){
        name = spritename+std::to_string((long long)spritecount);
        spritecount++;
    }

    sprites[name] = spt;
    return &sprites[name];
}

VisibleGameObject 在使用时似乎可以正常工作,而无需使用最初描述为问题的 setSprite 函数更改 sprite。

【问题讨论】:

  • 这是一个多线程程序吗? sprite 是可以从另一个线程访问的变量,并且在您执行此写入的同时被读取?
  • 你能说明精灵是在哪里定义和初始化的吗?
  • 另外,你是如何创建你正在传递的shared_ptr&lt;sf::Sprite&gt; 的?如果 sf::Sprite 保存在地图中,为什么要将其放入 shared_ptr 中?
  • 戴夫 S:见编辑。它作为资源系统的 shared_ptr 保存在 ResourceManager 中的静态地图中,但实际对象(VisibleGameObject)也需要跟踪它。

标签: c++ visual-studio-2010 c++11 shared-ptr sfml


【解决方案1】:

你失败了,因为你正在创建一个std::map&lt;std::string,sf::Sprite&gt;,它实际上拥有'sf::Sprite'对象。然后,您将指向映射中的对象的指针指向std::shared_ptr&lt;sf::Sprite&gt;,并使用默认删除器。

这意味着当对共享指针的最后一个引用超出范围时,它将在对象上调用delete。但是,该对象是地图的一部分。这会导致未定义的行为(在这种情况下是断言)。

有几种可能的解决方案:

1) 在这种情况下不要使用 shared_ptr。由于sf::Sprite 的所有权始终属于地图,因此您可以根本不用理会 shared_ptr 而是使用普通指针。

2) 使用不执行任何操作的自定义删除器。如果您有兴趣隐藏sf::Sprite 归地图所有的事实(假设在某些情况下您想即时创建它),那么您需要创建一个空删除函数。这里我使用的是 lambda,但您可以创建自己的函数。当最后一个引用超出范围时,这会导致 shared_ptr 什么都不做。由于内存并不真正由 shared_ptr 拥有,因此这是您想要的行为。

sprite = std::shared_ptr<sf::Sprite>(ResourceManager::createSpriteFromStoredTexture(textureName,name), [](const void*){} );

编辑:

实际上,我会修改第二个选项。我不会返回原始指针,而是让createSpriteFromStoredTexture 方法返回 shared_ptr,然后在其中使用 noop 删除器。这样,函数的用户并不知道sprite shared_ptr是如何创建的,它只知道此时它有一个shared_ptr。

3) 使用shared_ptr&lt;sf::Sprite&gt; 的地图。地图将始终拥有精灵,但它使 shared_ptr 的使用更加自然。

【讨论】:

  • 很好的答案,谢谢。没有注意到 VisibleGameObject 中的 shared_ptr 是最后一个引用,因为它存储在 ResourceManager 中。让 ResourceManager 的地图包含 shared_ptr<:sprite> 而不是 sf::Sprite 会是一个好的解决方案吗?
  • @猪头。那也行。让我将它添加到我的答案中,因为它也是一个合理的解决方案
猜你喜欢
  • 2011-09-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-07-31
相关资源
最近更新 更多