我在理解 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,所以两个参数都是key
和 texture
已被复制构建,因此拷贝已存储在返回的 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/