Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/powerbi/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ C++;11智能指针语义_C++_Pointers_C++11_Smart Pointers - Fatal编程技术网

C++ C++;11智能指针语义

C++ C++;11智能指针语义,c++,pointers,c++11,smart-pointers,C++,Pointers,C++11,Smart Pointers,我使用指针已经有几年了,但直到最近我才决定转向C++11的智能指针(即唯一、共享和弱)。我对它们做了一些研究,得出以下结论: 独特的指针非常棒。它们管理自己的内存,并且与原始指针一样轻量级。与原始指针相比,尽可能选择唯一的\u ptr 共享指针很复杂。由于引用计数,它们有很大的开销。通过不断的参考传递它们,或者为自己的错误感到遗憾。它们不是邪恶的,但应该谨慎使用 共享指针应该拥有对象;当不需要所有权时,使用弱指针。锁定弱\u ptr的开销与共享\u ptr复制构造函数的开销相当 继续忽略auto

我使用指针已经有几年了,但直到最近我才决定转向C++11的智能指针(即唯一、共享和弱)。我对它们做了一些研究,得出以下结论:

  • 独特的指针非常棒。它们管理自己的内存,并且与原始指针一样轻量级。与原始指针相比,尽可能选择唯一的\u ptr
  • 共享指针很复杂。由于引用计数,它们有很大的开销。通过不断的参考传递它们,或者为自己的错误感到遗憾。它们不是邪恶的,但应该谨慎使用
  • 共享指针应该拥有对象;当不需要所有权时,使用弱指针。锁定弱\u ptr的开销与共享\u ptr复制构造函数的开销相当
  • 继续忽略auto_ptr的存在,它现在已经被弃用了
  • 因此,牢记这些原则,我开始修改我的代码库,以利用我们新的闪亮的智能指针,充分打算清除尽可能多的原始指针。然而,对于如何最好地利用C++11智能指针,我感到困惑

    让我们假设,例如,我们正在设计一个简单的游戏。我们决定将虚构的纹理数据类型加载到TextureManager类中是最佳选择。这些纹理很复杂,因此无法按值传递它们。此外,让我们假设游戏对象需要特定的纹理,这取决于它们的对象类型(即汽车、船等)

    在此之前,我会将纹理加载到一个向量(或其他容器,如无序_贴图)中,并在每个游戏对象中存储指向这些纹理的指针,以便它们在需要渲染时可以引用它们。让我们假设纹理保证比指针寿命长

    那么,我的问题是如何在这种情况下最好地利用智能指针。我看到的选择很少:

  • 将纹理直接存储在容器中,然后在每个游戏对象中构造一个唯一的_ptr

    class TextureManager {
      public:
        const Texture& texture(const std::string& key) const
            { return textures_.at(key); }
      private:
        std::unordered_map<std::string, Texture> textures_;
    };
    class GameObject {
      public:
        void set_texture(const Texture& texture)
            { texture_ = std::unique_ptr<Texture>(new Texture(texture)); }
      private:
        std::unique_ptr<Texture> texture_;
    };
    
    类纹理管理器{
    公众:
    常量纹理和纹理(常量标准::字符串和键)常量
    {return.at(key);}
    私人:
    std::无序的贴图纹理;
    };
    类游戏对象{
    公众:
    空集纹理(常量纹理和纹理)
    {texture_uu=std::unique_ptr(新纹理(纹理));}
    私人:
    标准::独特的纹理;
    };
    
    然而,我对此的理解是,一个新的纹理将从传递的引用中复制构建,然后由unique_ptr拥有。这给我的印象是非常不受欢迎的,因为我会拥有和使用它的游戏对象一样多的纹理副本——击败指针点(没有双关语)

  • 不直接存储纹理,而是将其共享指针存储在容器中。使用make_shared初始化共享指针。在游戏对象中构造弱指针

    class TextureManager {
      public:
        const std::shared_ptr<Texture>& texture(const std::string& key) const
            { return textures_.at(key); }
      private:
        std::unordered_map<std::string, std::shared_ptr<Texture>> textures_;
    };
    class GameObject {
      public:
        void set_texture(const std::shared_ptr<Texture>& texture)
            { texture_ = texture; }
      private:
        std::weak_ptr<Texture> texture_;
    };
    
    类纹理管理器{
    公众:
    const std::shared_ptr&texture(const std::string&key)const
    {return.at(key);}
    私人:
    std::无序的贴图纹理;
    };
    类游戏对象{
    公众:
    空集纹理(常量标准::共享纹理)
    {texture\=texture;}
    私人:
    标准:弱纹理;
    };
    
    与独特的_ptr不同,我不必复制构建纹理本身,但渲染游戏对象的成本很高,因为每次我都必须锁定弱的_ptr(与复制构建新的共享_ptr一样复杂)

  • 总而言之,我的理解是这样的:如果我要使用唯一的指针,我必须复制构造纹理;或者,如果我要使用共享指针和弱指针,我必须在每次绘制游戏对象时复制和构造共享指针

    我知道智能指针天生比原始指针更复杂,所以我肯定会在某些方面有所损失,但这两种成本似乎都比它们应该的更高

    有人能给我指一下正确的方向吗


    抱歉读了这么长时间,谢谢你抽出时间

    即使在C++11中,原始指针作为对对象的非所有者引用仍然是完全有效的。在你的例子中,你说的是“让我们假设纹理保证比它们的指针寿命长。”这意味着在游戏对象中使用纹理的原始指针是完全安全的。在纹理管理器中,可以自动(在保证内存中恒定位置的容器中)存储纹理,也可以存储在
    unique_ptr
    s的容器中


    如果指针保证的有效期无效,则将纹理存储在管理器中的
    shared_ptr
    中,并在游戏对象中使用
    shared_ptr
    s或
    弱_ptr
    s,这取决于游戏对象对纹理的所有权语义。您甚至可以将其反转—在对象中存储
    shared_ptr
    s,在管理器中存储
    weak_ptr
    s。这样,管理器将充当缓存—如果请求纹理且其
    弱\u ptr
    仍然有效,它将给出该纹理的副本。否则,它将加载纹理,发出一个
    共享的\u ptr
    并保持一个
    弱的\u ptr

    您可以在存储纹理的位置有一个std::unique的std::map。然后,您可以编写一个get方法,通过名称返回对纹理的引用。这样,如果每个模型都知道其纹理的名称(它应该知道),则可以简单地将该名称传递到get方法中,并从贴图中检索引用

    class TextureManager 
    {
      public:
        Texture& get_texture(const std::string& key) const
            { return *textures_.at(key); }
      private:
        std::unordered_map<std::string, std::unique_ptr<Texture>> textures_;
    };
    
    类纹理管理器
    {
    公众:
    纹理和获取纹理(常量标准::字符串和键)常量
    {return*纹理在(键);}
    私人:
    std::无序的贴图纹理;
    };
    
    然后,您可以在游戏对象类中使用纹理,而不是纹理*,weak_ptr et
    using TextureRef = Texture const*;
    ...
    TextureRef TextureManager::texture(const std::string& key) const;
    
    TextureRef t{texture_manager.texture("grass")};
    
    // You can treat t as a value. You can pass it, return it, compare it,
    // or put it in a container.
    // But you use it like a pointer.
    
    double aspect_ratio{t->get_aspect_ratio()};
    
    struct Texture
    {
      Texture(std::string const& texture_name):
        pimpl_{texture_manager.texture(texture_name)}
      {
        // Either
        assert(pimpl_);
        // or
        if (not pimpl_) {throw /*an appropriate exception */;}
        // or do nothing if TextureManager::texture() throws when name not found.
      }
      ...
      double get_aspect_ratio() const {return pimpl_->get_aspect_ratio();}
      ...
      private:
      TextureImpl const* pimpl_; // invariant: != nullptr
    };
    
    Texture t{"grass"};
    
    // t has both value semantics and value syntax.
    // Treat it just like int (if int had member functions)
    // or like std::string (except lighter weight for copying).
    
    double aspect_ratio{t.get_aspect_ratio()};