移动资源管理器类的语义

Move semantics for a resource manager class

本文关键字:语义 资源管理器 移动      更新时间:2023-10-16

我正在尝试为我的游戏创建一个资源类(它使用SFMLneneneba API)。基本上,我首先加载所需的资源,然后在需要时获取对它们的引用,以避免资源类的繁重构建。

(我将使用sf::Texture类作为示例)

问题出在负载函数上。为了制作sf::Texture,我必须使用默认的构造函数,然后使用它的loadFromFile函数来获得所需的纹理。假设我有一个包含成对文件名和相应资源的映射:

std::map<std::string, sf::Texture> textures;
sf::Texture texture;
texture.loadFromFile(file);

现在,如果我这样做:

textures.emplace(file, texture);

这将使用复制构造函数来制作另一个sf::Texture。所以我想我应该使用std::move,但显然sf::Texture类没有move构造函数,所以这仍然是一个副本。为了解决这个问题,我现在使用一个包含文件名对和对象各自的unique_pointer的映射。所以我做了:

std::unique_ptr<sf::Texture> texture(new sf::Texture);
texture->loadFromFile(file);
textures.emplace(file, std::move(texture));

以下是完整功能的样子:

void ResourceManager::loadTexture(std::string file)
{
        std::unique_ptr<sf::Texture> texture(new sf::Texture);
        if (!texture->loadFromFile(file))
        {
                throw std::runtime_error("ResourceManager::load - Failed to load " + file);
        }
        auto inserted = mtextures.emplace(file, std::move(texture));
        assert(inserted.second);
}

不过,我在移动语义方面没有太多经验,也不确定我是否一切都对。基本上,我错过了什么吗?如果这个问题不属于堆栈溢出,我会很乐意将其转移到适当的站点。

您发布的代码看起来是正确的,给出了您尝试做什么的描述。

地图将被指定为资源的所有者。当它被销毁时,资源也将被销毁,除非有人事先检索到资源(例如,通过std::move)。

我唯一的建议是在创建指针时使用std::make_unique(为了异常安全,并使代码更惯用)。除了这些小细节,听起来你的想法是对的。

如果您担心不能有效地移动sf::Texture,那么就不要移动sf::Texture。直接在地图中构建它们:

void ResourceManager::loadTexture(const std::string& file)
{
    assert(mtextures.find(file) == mtextures.end());
    auto& t = mtextures[file];
    try {
        if (!t.loadFromFile(file)) {
            throw std::runtime_error("ResourceManager::load - Failed to load " + file);
        }
    } catch(...) {
        mtextures.erase(file);
        throw;
    }
}