【问题标题】:Making shared_ptr lose ownership of memory使 shared_ptr 失去内存所有权
【发布时间】:2018-06-15 18:25:57
【问题描述】:

我有一个shared_ptr<MyProto>,我传递了它。最终,在某些情况下,我想将原始指针传递给一个函数,然后该函数成为内存所有者。在这些情况下,shared_ptr 不再负责释放内存,因为我调用的函数取得了所有权。如何让shared_ptr 失去所有权?

我想让 shared_ptr 失去所有权的原因是我想使用协议缓冲区的 AddAllocated 功能,该功能采用已分配的指针并承担它的所有权。

例子:

shared_ptr<MyProto> myProtoSharedPtr = // by this point this is the last reference to the heap allocated MyProto

// I want to add it to a collection and serialize the collection without copying
CollectionProto collectionProto;
collectionProto.mutable_my_proto().AddAllocated(myProtoSharedPtr.get()); // at this point collectionProto took ownership of the memory
std::string serialization = collectionProto.SerializeAsString();

// bad: myProtoSharedPtr.get() will be freed twice

【问题讨论】:

  • 你为什么使用共享指针?
  • 这没有意义..这个shared_ptr要释放所有权,其他shared_ptrs共享所有权怎么样?
  • 您必须从拥有它的每个 std::shared_ptr 释放所有权。这对 imo 来说不是一件很安全的事情。如果错过了怎么办?
  • @Hitobat 如果这是最后一个 shared_ptr,这将释放内存。
  • 你想要的几乎是不可能的,因为上面提到的关于共享所有权的原因,但也因为你不知道内存是如何分配的。如果共享指针是使用make_shared 创建的,您可能有一个内存块同时保存对象和控制块,因此您必须用对象销毁控制块,但如果它是一个,您会怎么做或者如果你只有一个指向对象的指针?

标签: c++ c++11 protocol-buffers shared-ptr


【解决方案1】:

我认为你可以通过分享一个独特的来实现你想做的事情 像这样的指针

std::shared_ptr<std::unique_ptr<MyProto>> myProtoSharedUniquePtr;

访问它会更间接:

(*myProtoSharedUniquePtr)->do_stuff();

但您可以像这样获得所有权:

CollectionProto collectionProto;
collectionProto.mutable_my_proto().AddAllocated(myProtoSharedUniquePtr->release()); // at this point collectionProto took ownership of the memory
std::string serialization = collectionProto.SerializeAsString();

但是我会质疑您为什么要使用 std::shared_ptr 开头。使用std::shared_ptr 的原因是当您无法控制谁将最后访问它时,因此每个人都可以在完成之前使其保持活动状态。因此,能够保证所有当前的 std::shared_ptr 实例都不再使用是不寻常的。

您确定std::unique_ptr 不会更好地满足您的需求吗?

【讨论】:

    【解决方案2】:

    您可以使用unique_ptr,无论如何它更适合传递内存:

    unique_ptr<MyProto> myProtoSharedPtr = // create MyPorto object
    
    CollectionProto collectionProto;
    
    // unique_ptr::release returns the pointer and
    // releases the ownership of the MyProto object
    collectionProto.mutable_my_proto().AddAllocated(myProtoSharedPtr.release());
    
    std::string serialization = collectionProto.SerializeAsString();
    

    【讨论】:

      【解决方案3】:

      您需要为std::shared_ptr 提供custom deleter(参见构造函数4)。然后你可以定义删除器来做你想做的事。包括,而不是破坏你的对象。

      注意 1:我不建议在这里使用 shared_ptr,但这是一种做你想做的事情的方法。

      注意 2:如果您使用 make_shared 创建对象,则在删除最后一个 shared_ptr 后,您可能会遇到正确删除内存的问题。

      【讨论】:

      • 关于您的第二条注释:make_shared 不支持自定义删除器正是出于这个原因。使用allocate_shared 仍然可以进行单一分配,并将“删除”逻辑放在分配器中,但不幸的是,shared_ptr 的公共 API 不支持分配器访问,在这种情况下,release()。一种解决方法是在对象本身 IOW 中放置一个shouldDelete 标志,使用更大的鞋拔...
      【解决方案4】:

      您可以将其移动到newed 对象中,而不是复制。

      MyProto * myProto = new MyProto(std::move(*mySharedProto));
      
      CollectionProto collectionProto;
      collectionProto.mutable_my_proto().AddAllocated(myProto);
      

      你也可以调查CollectionProto是否会按值接受它

      CollectionProto collectionProto;
      collectionProto.mutable_my_proto().Add(std::move(*mySharedProto));
      

      【讨论】:

        【解决方案5】:

        当你想转移所有权时,你可以使用std::move,见下面的例子

        #include <iostream>
        #include <memory>
        void take_ownership(std::shared_ptr<int> ptr){
        std::cout<<ptr.use_count()<<" == 2\n";
        } // destroying it
        
        
        int main()
        {
        std::shared_ptr<int> p=std::make_shared<int>(1);
        std::shared_ptr<int> p2(p);
        //p is valid                                                                                                                                                                                                                                                                              
        if(!p.get())
        std::cout<<"error\n";
        else
        std::cout<<"OK\n";
        
        //use p, p2
        
        take_ownership(std::move(p));
        //p is invalid                                                                                                                                                                                                                                                                            
        if(!p.get())
        std::cout<<"OK\n";
        else
        std::cout<<p.use_count()<<" error\n";
        
        }
        

        【讨论】:

        • 我对这个问题的回答是使用 std::move 来转移共享所有权。使用weak_ptr 只会让事情变得混乱,在这个例子中没有必要。
        猜你喜欢
        • 2012-02-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-12-25
        • 1970-01-01
        • 2021-09-22
        • 2010-09-14
        • 1970-01-01
        相关资源
        最近更新 更多