C++ 资源管理器类的移动语义

C++ 资源管理器类的移动语义,c++,c++11,resources,sfml,move-semantics,C++,C++11,Resources,Sfml,Move Semantics,我正在尝试为我的游戏创建一个资源类(它使用SFMLAPI)。 基本上,我首先加载所需的资源,然后在需要时获取它们的引用,以避免资源类的繁重构造 (我将使用sf::Texture类作为示例) 问题在于加载函数。 为了生成sf::Texture,我必须使用默认构造函数,然后使用它的loadFromFile函数来获得所需的纹理。假设我有一个包含成对文件名和相应资源的映射: std::map<std::string, sf::Texture> textures; sf::Texture te

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

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

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

std::map<std::string, sf::Texture> textures;
sf::Texture texture;
texture.loadFromFile(file);
这将使用复制构造函数生成另一个sf::Texture。 所以我想我应该使用std::move,但是显然sf::Texture类没有move构造函数,所以它仍然可以复制。为了解决这个问题,我现在使用一个包含成对文件名和指向对象的各自唯一指针的映射。 因此,我:

std::unique_ptr纹理(新sf::纹理);
纹理->加载源文件(文件);
emplace(文件,std::move(纹理));
以下是完整函数的外观:

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);
}
void ResourceManager::loadTexture(std::string文件)
{
std::unique_ptr纹理(新sf::纹理);
如果(!texture->loadFromFile(文件))
{
抛出std::runtime_错误(“ResourceManager::load-加载失败”+文件);
}
自动插入=mtextures.emplace(文件,std::move(纹理));
断言(插入。秒);
}

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

根据您尝试执行的操作的描述,您发布的代码看起来是正确的

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


我唯一的建议是在创建指针时使用它(为了异常安全,并使代码更加习惯化)。除了那个小细节,听起来你的想法是对的。

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

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


我唯一的建议是在创建指针时使用它(为了异常安全,并使代码更加习惯化)。除了那个小细节,听起来你的想法是对的。

如果你担心不能有效地移动
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;
    }
}

如果您担心无法有效地移动
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;
    }
}


显然sf::Texture类没有移动构造函数
。实际上,SFML2是用C++98编写的。
显然,sf::Texture类没有移动构造函数。实际上,SFML2是用C++98编写的。make_是C++11标准的一部分吗?我认为它还没有在库中实现,它是C++14标准的一部分,但是所有主要的编译器都已经实现了它。如果它在编译器上不可用,您应该使用Herb Sutter提供的这个自定义实现:它似乎在GCC的4.8版本中不可用,但正在4.9版本中实现。Dang,我可以发誓GCC已经有了它。对不起,误导了你。(我确实知道Clang和Visual Studio拥有它。)没关系,我可能会自己实现它,直到它在你的甜蜜链接的帮助下发布:LIs使_成为c++11标准的一部分?我认为它还没有在库中实现,它是C++14标准的一部分,但是所有主要的编译器都已经实现了它。如果它在编译器上不可用,您应该使用Herb Sutter提供的这个自定义实现:它似乎在GCC的4.8版本中不可用,但正在4.9版本中实现。Dang,我可以发誓GCC已经有了它。对不起,误导了你。(我知道Clang和VisualStudio拥有它。)没关系,我可能会自己实现它,直到它在你的甜蜜链接的帮助下发布:这是完美的,让我觉得自己很傻。我忘了[]操作符在地图中引入了一个新元素,如果地图中没有一个元素具有相同的密钥,那么我想我只能使用insert和emplace插入一对。顺便问一下,这不是应该的!=在断言中?@Veritas
find
使用给定键(
file
)或
end()
返回一个迭代器,该项不存在。
assert
验证我们要添加的条目事先不在映射中,就像
assert(inserted.second)一样
在OP中。当你开始把条件语句弄错时,你知道该睡觉了。对于loadFromFile函数,这个版本似乎不是那么安全。这很完美,让我觉得自己很傻。我忘了[]操作符在地图中引入了一个新元素,如果地图中没有一个元素具有相同的密钥,那么我想我只能使用insert和emplace插入一对。顺便问一下,这不是应该的!=在断言中?@Veritas
find
使用给定键(
file
)或
end()
返回一个迭代器,该项不存在。
assert
验证我们要添加的条目事先不在映射中,就像t中的
assert(inserted.second)
一样
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;
    }
}