共享指针导致奇怪的行为

Shared pointers causing weird behaviour

本文关键字:指针 共享      更新时间:2023-10-16

我在SFML 2.1中有以下代码

ResourceManager类:

shared_ptr<Sprite> ResourceManager::getSprite(string name) {
    shared_ptr<Texture> texture(new Texture);
    if(!texture->loadFromFile(resPath+spritesPath+name)) 
        throw new NotSuchFileException();
    shared_ptr<Sprite> sprite(new Sprite(*texture));
    return sprite;
}

主方法:(我将省略大部分不相关的代码

shared_ptr<Sprite> sprite = ResourceManager::getSprite("sprite.png");
...
while(renderWindow.isOpen()) 
    renderWindow.draw(*sprite);

奇怪的是,这会使我的精灵完全呈现白色,但如果我这样做:

shared_ptr<Sprite> ResourceManager::getSprite(string name) {
    Texture* texture = new Texture; // <------- From shared pointer to pointer
    if(!texture->loadFromFile(resPath+spritesPath+name)) 
        throw new NotSuchFileException();
    shared_ptr<Sprite> sprite(new Sprite(*texture));
    return sprite;
}

效果很好。

这里发生了什么?我假设共享指针就像指针一样工作。会不会是它被删除了?我的主要方法是保持对它的引用所以我真的不明白这里发生了什么:S

编辑:我完全知道删除精灵不会删除纹理,这是产生一个内存泄漏,我必须处理,这就是为什么我试图使用智能指针在第一个地方…

我可能离得太远了,但我认为值得一试:

如果精灵类构造函数接受一个常规的纹理*或一个纹理引用(与它接受和存储一个共享指针相反),那么当shared_ptr超出getSprite的作用域时,纹理将被销毁(因为在该点之后没有shared_ptr的纹理存在)。

你在第一个中的共享指针超出了作用域并删除了对加载纹理的引用。为了保留该实例,您需要保存对sf::Texture实例的引用,因此您需要这样做(未经测试的代码):

std::map<std::string, std::unique_ptr<sf::Texture>> mLoadedTextures;
std::shared_ptr<sf::Sprite> ResourceManager::getSprite(std::string name) {
    auto found = mLoadedTextures.find(name);
    if (found == mLoadedTextures.end()) {
        std::unique_ptr<sf::Texture> texture(new sf::Texture());
        if (!texture->loadFromFile(resPath+spritesPath+name)) {
            // ERROR: Unable to find/load texture
        }
        auto inserted = mLoadedTextures.insert(std::make_pair(name, std::move(texture)));
        if (!inserted.second)) {
            // ERROR: Unable to insert into map
        }
        found = inserted.first;
    }
    shared_ptr<sf::Sprite> sprite(new sf::Sprite(*found.second));
    return sprite;
}

你应该重新考虑你的ResourceManager是如何运作的。理想情况下,你应该预先完成所有加载,并将所有纹理放在资源管理器拥有的地图中。这样,创建精灵就不会因为在"关键路径"上加载纹理而影响性能。

如果你读过"SFML游戏开发",他们会很好地涵盖这个主题。

http://www.packtpub.com/sfml-game-development/book

对象的sf::Sprite是相当轻量级的(它真的只是一个容器的四元和一些更多的信息),所以我甚至不会打扰他们在某种资源管理器管理。

更重的对象是纹理,所以只管理那一个。

如前所述,纹理的智能指针将超出作用域,因此纹理将被删除。

即使游戏中的每个实体都有自己的精灵,它也不会对性能造成太大影响(与重复使用一个精灵相比)。

所以,为了解决你的问题,管理纹理基于他们的路径,而不是精灵。