c++ - 当通过 const 引用传递的对象被添加到像 std::map 这样的容器时会发生什么?

标签 c++ c++11 dictionary std sfml

我在理解 C++ 中对象所有权的概念时遇到问题。

为了让您了解一些背景知识,我正在使用 Visual Studio 2013 在 Windows 上使用 SFML 2.1 在 C++ 11 中编写一个小应用程序。

基本上我写了一个类 AssetsManager 来环绕我的 sf::Texture 对象。我想将我的纹理存储在 std::map 中,以便能够使用一个键轻松地引用和找到它们。该 map 是 AssetsManager 类的私有(private)成员。

我有两个公共(public)成员函数,用于将纹理对象“注册”(添加)到 map 和从 map 获取纹理对象。

这是我在类头中声明 map 的方式:

private:
    // Resources are mapped to a key for easy access.
    std::map < std::string, sf::Texture > m_textures;

这是cpp文件中的两个函数定义:

sf::Texture AssetsManager::GetTexture(const std::string& key)
{
    return m_textures[key];
}

bool AssetsManager::RegisterTexture(const std::string& key, const sf::Texture& texture)
{
    // If a texture in the std::map already exists with the given key, return false.
    if (m_textures.count(key))
        return false;

    // Add the texture with key to the std::map.
    m_textures.insert(std::make_pair(key, texture));

    return true;
}

在我的主要功能中,我这样做:

AssetsManager assets;

sf::Texture myTexture;
myTexture.loadFromFile("Assets/MyCharacter.png");
assets.RegisterTexture("Hero Character", myTexture);

我不明白的是在我的主函数中声明的 myTexture 变量发生了什么。 我将对象添加到 map 中,所以我可以通过调用

来获取它
assets.GetTexture("Hero Character");

我可以在 main 函数中使用 myTexture 变量做些什么吗?

两个对象是同一个吗?或者所有权是否从 myTexture 转移到 map 中的对象,从而使 myTexture 变得有点空?

如果有人能澄清这一点,那就太好了,这样我就可以继续我的工作了。

如果在这里使用指针是正确的选择,我什至会犹豫不决,但我希望 AssetsManager 能够完全控制 Textures,即“拥有”它们,而不仅仅是指向对象的指针,如果你明白我的意思的话。

最佳答案

逐步分析:

m_textures.insert(std::make_pair(key, texture));

首先,std::make_pair(T&&, U&&) takes forwarding-references,并实例化一对 decayed类型:std::pair<std::decay_t<T>, std::decay_t<U>>从提供的参数。在您的情况下,这些类型如下:

T = const std::string&, std::decay_t<T> = std::string
U = const sf::Texture&, std::decay_t<U> = sf::Texture

话虽如此,该对定义为 std::pair<std::string, sf::Texture> ,并且由于原始值是const lvalue references,所以两个参数都是keytexture已被复制构建,因此拷贝已存储在返回的 std::pair 的实例中.

现在,insert成员函数。 std::map有它的overload取另一个forwarding-reference,描述如下:

Value to be copied to (or moved as) the inserted element.

也就是说,insert 的论点(推导类型 P = std::pair<std::string, sf::Texture> ,带 P&& )是 std::forward<P>编辑到新创建的节点对,您的 std::map 实例商店。这导致移动你的对,描述为:

The object is initialized with the contents of the pr pair object. The corresponding member of pr is passed to the constructor of each of its members.

但是,如果这些成员中的任何一个没有实现移动构造函数,操作将退回到该特定成员的常规复制构造

总结一下,用m_textures.insert(std::make_pair(key, texture));最坏情况中声明,您最终可能会得到两个(可能是昂贵的)两个论点的拷贝。唯一的好处是您不必担心所有权,因为std::map本身获得了这些元素的全新拷贝。附带说明一下,当有人通过引用 获取您的对象时,您实际上永远不必担心所有权。甚至不太可能有人会考虑存储对此类对象的引用或地址,因为它的来源是未知的,而且人们无法控制它的生命周期。

您可以尝试优化此操作,将其替换为以下 emplace 成员函数的调用:

m_textures.emplace(std::piecewise_construct
                 , std::forward_as_tuple(key)
                 , std::forward_as_tuple(texture));

这将使用参数将一对直接放置到关联的节点中。

尽管如此,我觉得你应该使用 std::shared_ptr对于 sf::Texture实例,因为顾名思义,它可以存储大量数据。

此外,考虑重新实现您的 GetTexture成员函数(具有重载的 const-qualified 一个)返回reference:

sf::Texture& AssetsManager::GetTexture(const std::string& key)
//~~~~~~~~~^
{
    return m_textures[key];
}

const sf::Texture& AssetsManager::GetTexture(const std::string& key) const
//~~~~~~~~~~~~~~~^                                                   ~~~~^
{
    return m_textures.at(key);
}

因为否则每次调用 GetTexture也会制作拷贝。

关于c++ - 当通过 const 引用传递的对象被添加到像 std::map 这样的容器时会发生什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26430146/

相关文章:

c++ - 一次覆盖两个方法

c++ - 通过基类完美转发

python - 错误: nothing to repeat at position

c - C : use int and struct to determine a value 中的类映射结构

c++ - 无法理解带有两个变量的循环

c++ - 在 Windows C++ 中禁用键盘?

c++ - 使用 Arduino 上的 atoi 和 itoa 从 int 转换为字节数组然后返回 int 进行传输

c++ - C++中的循环继承

c++ - enable_shared_from_this 与直接赋值

ios LINESTRING 解析