【问题标题】:Move semantics for a resource manager class资源管理器类的移动语义
【发布时间】:2014-06-02 20:59:50
【问题描述】:

我正在尝试为我的游戏创建一个资源类(它使用 SFML 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 类没有移动构造函数,所以它仍然会复制。为了解决这个问题,我现在使用一个包含文件名对和对象的各自唯一指针的映射。 所以我这样做:

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);
}

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

【问题讨论】:

  • apparently the sf::Texture class doesn't have a move constructor。事实上,SFML 2 是用 C++98 编写的。

标签: c++ c++11 resources sfml move-semantics


【解决方案1】:

鉴于您正在尝试执行的操作的描述,您发布的代码看起来是正确的。

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

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

【讨论】:

  • make_unique 是 c++11 标准的一部分吗?我以为它还没有在库中实现。
  • 它是 C++14 标准的一部分,但所有主要编译器都已经实现了它。如果它在您的编译器上不可用,您应该使用 Herb Sutter 提供的这个自定义实现:herbsutter.com/gotw/_102
  • 似乎在 GCC 的 4.8 版本中不可用,但它正在 4.9 版本中实现。
  • 该死,我可以发誓 GCC 已经有了它。很抱歉误导了你。 (我知道 Clang 和 Visual Studio 都有。)
  • 没关系,我可能会自己实现它,直到它在你的那个甜蜜链接的帮助下发布:L
【解决方案2】:

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

【讨论】:

  • 这是完美的,让我觉得自己很笨。我忘记了 [] 运算符会在映射中引入一个新元素,如果其中没有一个具有相同键的元素,所以我想我只能使用 insert 和 emplace 插入一对。
  • 顺便说一句,断言中不应该是 != 吗?
  • @Veritas find 将迭代器返回到具有给定键的条目 (file) 或 end() 不存在这样的条目。 assert 验证我们添加的条目事先不存在于地图中,就像 OP 中的 assert(inserted.second) 一样。
  • 当你开始弄错条件句时,你就知道该睡觉了。
  • 看来这个版本在 loadFromFile 函数方面并不是那么安全。
猜你喜欢
  • 2016-03-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-04-15
  • 2017-10-29
  • 1970-01-01
相关资源
最近更新 更多