【问题标题】:How to release pointer from boost::shared_ptr?如何从 boost::shared_ptr 释放指针?
【发布时间】:2010-12-04 06:56:44
【问题描述】:

boost::shared_ptr 可以释放存储的指针而不删除它吗?

我可以看到文档中不存在释放功能,在FAQ中也解释了为什么它不提供释放功能,例如不能对不​​唯一的指针进行释放。我的指针是独一无二的。我怎样才能释放我的指针? 或者使用哪个 boost 智能指针类可以让我释放指针? 我希望你不要说使用 auto_ptr :)

【问题讨论】:

  • 为什么不auto_ptr?如果它们是唯一的,则必须意味着它们永远不会被复制(因为随后会存在多个引用,即使只是暂时的),然后auto_ptr 应该可以正常工作。或者,如果您不打算使用智能指针提供的生命周期管理无论如何,请使用原始指针。
  • 除了引用计数语义之外,shared_ptr 还提供了一个 auto_ptr 没有的自定义删除器工具。所以这是一个场景:您使用自定义分配器(即不是全局新建/删除)创建一个对象,并且在配置对象时需要一个智能指针以确保异常安全,但是一旦完成就需要返回一个原始指针做可能会抛出的事情。不幸的是,auto_ptr 和任何 boost smart_ptrs 似乎都不支持这一点。
  • 第 3 方界面存在此问题。一些接口从工厂返回一个“唯一的”shared_ptr,因为有一个案例证明这是 C++11 之前的最佳方式。抛出 shared_ptr -> unique_ptr 转换可能很有用,即使你真的想打破规则也不能打破规则,这很痛苦!

标签: c++ boost shared-ptr


【解决方案1】:

不要。 Boost 的常见问题解答条目:

。为什么 shared_ptr 不提供 release() 函数?

一个shared_ptr 不能放弃所有权,除非它是 unique(),因为另一个副本仍然会破坏该对象。

考虑:

shared_ptr<int> a(new int);
shared_ptr<int> b(a); // a.use_count() == b.use_count() == 2

int * p = a.release();

// Who owns p now? b will still call delete on it in its destructor.

此外,release() 返回的指针很难可靠地释放,因为源 shared_ptr 可以使用自定义删除器创建。

因此,如果它是唯一指向您的对象的 shared_ptr 实例(当 unique() 返回 true 时)并且该对象不需要特殊的删除器,这将是安全的。如果您使用这样的 .release() 函数,我仍然会质疑您的设计。

【讨论】:

  • 理想情况下,只有当 use_count() == 1 时,release 才会成功。这样可以。 :|
【解决方案2】:

您可以使用假删除器。那么指针实际上不会被删除。

struct NullDeleter {template<typename T> void operator()(T*) {} };

// pp of type some_t defined somewhere
boost::shared_ptr<some_t> x(pp, NullDeleter() );

【讨论】:

  • 这很有趣,但首先使用 shared_ptr 的目的是什么?
  • @UncleBens,这种删除器的使用非常有限。例如,当您需要调用一个函数并将类似shared_ptr&lt;some_t&gt;( this ) 的内容传递给它时。只有NullDeleter 才会安全。
  • 是的,当接口(我们无法控制)需要一个 shared_ptr 并保证它不会存储函数调用之后的指针时,我们使用了类似的东西。
  • 如果你可以使用空删除器,你可能不需要smart_ptr
【解决方案3】:

孩子们,不要在家里这样做:

// set smarty to point to nothing
// returns old(smarty.get())
// caller is responsible for the returned pointer (careful)
template <typename T>
T* release (shared_ptr<T>& smarty) {
    // sanity check:
    assert (smarty.unique());
    // only one owner (please don't play games with weak_ptr in another thread)
    // would want to check the total count (shared+weak) here

    // save the pointer:
    T *raw = &*smarty;
    // at this point smarty owns raw, can't return it

    try {
        // an exception here would be quite unpleasant

        // now smash smarty:
        new (&smarty) shared_ptr<T> ();
        // REALLY: don't do it!
        // the behaviour is not defined!
        // in practice: at least a memory leak!
    } catch (...) {
        // there is no shared_ptr<T> in smarty zombie now
        // can't fix it at this point:
        // the only fix would be to retry, and it would probably throw again
        // sorry, can't do anything
        abort ();
    }
    // smarty is a fresh shared_ptr<T> that doesn't own raw

    // at this point, nobody owns raw, can return it
    return raw;
}

现在,有没有办法检查 ref 计数的所有者总数是否 > 1?

【讨论】:

  • @Xeo No. See boost: use_count "Returns: shared_ptr 对象的数量,包括 *this,与 *this 共享所有权," 或 Visual Studio 2010: use_count "_returns 拥有 *this 所拥有资源的 shared_ptr 对象的数量。而 unique() tests exactly that: "use_count() == 1" 这不是这里需要的。@ 的另一个问题987654329@.
  • 那么,您对上一个问题还有什么要求?
  • +1 用于提供问题的答案,可惜没有更好的方法。
  • 让我指出这个解决方案的两个严重问题: 1> 如果您使用了make_shared,那么release 将不会返回一个可以与delete 一起使用的指针,因为make_shared 的方式分配内存。 2> weak_ptr 通常使用订阅模式,所以如果你不运行shared_ptr 析构函数,weak_ptrlock() 方法将返回一个有效的shared_ptr,它再次拥有内存。它可能不会删除它,因为引用计数现在减少了 1。
  • 而且它甚至继续saying“对于具有非平凡析构函数的类类型的对象,程序不需要在对象存储之前显式调用析构函数占用被重用或释放;但是,如果没有显式调用析构函数或者如果没有使用删除表达式来释放存储,则不应隐式调用析构函数,并且任何依赖于边的程序析构函数产生的效果具有未定义的行为。”
【解决方案4】:

您需要使用可以请求不删除底层指针的删除器。

See this answer(已标记为此问题的副本)了解更多信息。

【讨论】:

    【解决方案5】:

    要让指针再次指向空,可以调用shared_ptr::reset()

    但是,当您的指针是对该对象的最后一个引用时,这将删除指向的对象。然而,这正是智能指针所期望的行为。

    如果您只想要一个不保存对象的引用,您可以创建一个boost::weak_ptr(请参阅boost documentation)。 weak_ptr 持有对该对象的引用,但不会添加到引用计数中,因此当仅存在弱引用时,该对象将被删除。

    【讨论】:

      【解决方案6】:

      分享的基础是信任。如果您的程序中的某个实例需要释放原始指针,则几乎可以肯定shared_ptr 是错误的类型。

      但是,最近我也想这样做,因为我需要从不同的进程堆中释放。最后我被告知,我以前使用一些std::shared_ptr 的决定没有经过深思熟虑。

      我只是经常使用这种类型进行清理。但是指针只是在几个地方重复。实际上我需要一个std::unique_ptr,它(惊喜)有一个release 功能。

      【讨论】:

        【解决方案7】:

        原谅他们,因为他们不知道自己在做什么。 此示例与 boost::shared_ptr 和 msvs std::shared_ptr 一起使用,没有内存泄漏!

        template <template <typename> class TSharedPtr, typename Type>
        Type * release_shared(TSharedPtr<Type> & ptr)
        {
            //! this struct mimics the data of std:shared_ptr ( or boost::shared_ptr )
            struct SharedVoidPtr
            {
                struct RefCounter
                {
                    long _Uses;
                    long _Weaks;
                };
        
                void * ptr;
                RefCounter * refC;
        
                SharedVoidPtr()
                {
                    ptr = refC = nullptr;
                }
        
                ~SharedVoidPtr()
                {
                    delete refC;
                }
            };
        
            assert( ptr.unique() );
        
            Type * t = ptr.get();
        
            SharedVoidPtr sp; // create dummy shared_ptr
            TSharedPtr<Type> * spPtr = (TSharedPtr<Type>*)( &sp );
            spPtr->swap(ptr); // swap the contents
        
            ptr.reset();
            // now the xxx::shared_ptr is empy and
            // SharedVoidPtr releases the raw poiter but deletes the underlying counter data
            return t;
        }
        

        【讨论】:

          【解决方案8】:

          您可以删除共享指针,这对我来说似乎很相似。如果指针总是唯一的,那么std::auto_ptr&lt;&gt; 是一个不错的选择。请记住,唯一指针不能在 STL 容器中使用,因为对它们的操作会进行大量复制和临时复制。

          【讨论】:

          • @curiousguy:在这种情况下,我对“发布”的语义有点模糊,更不用说 OP 真正想要做什么了。
          • 简单:他已经转换了一个指向share_ptr的指针(用一些参数share_ptr构造了一个p),他想做反向操作(恢复share_ptr的构造) .
          【解决方案9】:

          我不完全确定您的问题是否与实现这一目标有关,但如果您想要来自 shared_ptr 的行为,其中,如果您从一个 shared_ptr 释放值,则所有其他共享指针指向相同的值成为一个 nullptr,然后你可以在 shared_ptr 中添加一个 unique_ptr 来实现该行为。

          void print(std::string name, std::shared_ptr<std::unique_ptr<int>>& ptr)
          {
              if(ptr == nullptr || *ptr == nullptr)
              {
                  std::cout << name << " points to nullptr" << std::endl;
              }
              else
              {
                  std::cout << name << " points to value " << *(*ptr) << std::endl;
              }
          }
          
          int main()
          {
              std::shared_ptr<std::unique_ptr<int>> original;
              original = std::make_shared<std::unique_ptr<int>>(std::make_unique<int>(50));
          
              std::shared_ptr<std::unique_ptr<int>> shared_original = original;
          
              std::shared_ptr<std::unique_ptr<int>> thief = nullptr;
          
              print(std::string("original"), original);
              print(std::string("shared_original"), shared_original);
              print(std::string("thief"), thief);
          
              thief = std::make_shared<std::unique_ptr<int>>(original->release());
          
              print(std::string("original"), original);
              print(std::string("shared_original"), shared_original);
              print(std::string("thief"), thief);
          
              return 0;
          }
          

          输出:

          original points to value 50
          shared_original points to value 50
          thief points to nullptr
          original points to nullptr
          shared_original points to nullptr
          thief points to value 50
          

          此行为允许您共享资源(如数组),然后在使对该资源的所有共享引用无效时重用该资源。

          【讨论】:

            【解决方案10】:

            这是一个可行的技巧。我不会推荐它,除非你是真正的绑定。

            template<typename T>
            T * release_shared(std::shared_ptr<T> & shared)
            {
                static std::vector<std::shared_ptr<T> > graveyard;
                graveyard.push_back(shared);
                shared.reset();
                return graveyard.back().get();
            }
            

            【讨论】:

            • @curiousguy,我不确定你的意思。这是来自 cplusplus.com 的auto_ptr::release 的定义:"Sets the auto_ptr internal pointer to null pointer (which indicates it points to no object) without destructing the object currently pointed by the auto_ptr"。这个函数就是这样做的; reset 将指针设置为 null,并将指针复制到静态墓地对象可防止 shared_ptr 删除它。
            • "这个函数可以做到这一点",但不仅如此。 release 的合同只做指定的事情。 “将指针复制到静态墓地”不是release 规范的一部分。
            • @curiousguy,没有记录的方法可以防止 shared_ptr 删除它指向的对象。正如我完全承认的那样,我的“hack”是让 smart_ptr 参考悬而未决而不会被破坏。这是在release_shared 函数之外不可见的实现细节。
            • 留下一个 smart_ptr 引用而不被破坏”我不知道static 变量永远不会被破坏。
            【解决方案11】:

            如果您的指针确实是唯一的,请使用 std::unique_ptrboost::scoped_ptr 如果前者不适用于您的编译器。否则考虑将boost::shared_ptrboost::weak_ptr 结合使用。详情请查看Boost documentation

            【讨论】:

              【解决方案12】:

              我正在使用 Poco::HTTPRequestHandlerFactory,它期望返回一个原始的 HTTPRequestHandler*,一旦请求完成,Poco 框架就会删除处理程序。

              同样使用 DI Sauce 项目来创建控制器,但是 Injector 返回 shared_ptr 我不能直接返回,并且返回 handler.get() 也不好,因为一旦这个函数返回 shared_ptr 超出范围并且在执行之前删除然后处理程序,所以这是一个合理的(我认为)有一个 .release() 方法的理由。我最终创建了一个 HTTPRequestHandlerWrapper 类,如下所示:-

              class HTTPRequestHandlerWrapper : public HTTPRequestHandler {
              private:
                  sauce::shared_ptr<HTTPRequestHandler> _handler;
              
              public:
                  HTTPRequestHandlerWrapper(sauce::shared_ptr<HTTPRequestHandler> handler) {
                      _handler = handler;
                  }
              
                  virtual void handleRequest(HTTPServerRequest& request, HTTPServerResponse& response) {
                      return _handler->handleRequest(request, response);
                  }
              };
              

              然后工厂会

              HTTPRequestHandler* HttpHandlerFactory::createRequestHandler(const HTTPServerRequest& request) {
                  URI uri = URI(request.getURI());
                  auto path = uri.getPath();
                  auto method = request.getMethod();
              
                  sauce::shared_ptr<HTTPRequestHandler> handler = _injector->get<HTTPRequestHandler>(method + ":" + path);
              
                  return new HTTPRequestHandlerWrapper(handler);
              }
              

              满足 Sauce 和 Poco 的要求,效果很好。

              【讨论】:

                【解决方案13】:

                我需要通过异步处理程序传递一个指针,并在发生故障时保持自毁行为,但最终的 API 需要一个原始指针,所以我让这个函数从单个 shared_ptr 中释放:

                #include <memory>
                
                template<typename T>
                T * release(std::shared_ptr<T> & ptr)
                {
                    struct { void operator()(T *) {} } NoDelete;
                    
                    T * t = nullptr;
                    if (ptr.use_count() == 1)
                    {
                        t = ptr.get();
                        ptr.template reset<T>(nullptr, NoDelete);
                    }
                    return t;
                }
                

                如果是ptr.use_count() != 1,您将获得nullptr


                请注意,来自cppreference(我的粗体强调):

                如果use_count 返回 1,则没有其他所有者。 (为此用例提供了已弃用的成员函数 unique()。)在多线程环境中,这并不意味着修改对象是安全的,因为以前的共享所有者可能无法访问托管对象已经完成,并且因为可能会同时引入新的共享所有者,例如 std::weak_ptr::lock

                【讨论】:

                  【解决方案14】:

                  简单的解决办法,增加引用然后泄露shared_pointer。

                  boost::shared_ptr<MyType> shared_pointer_to_instance(new MyType());
                  new boost::shared_ptr<MyType>();
                  MyType * raw_pointer = shared_pointer_to_instance.get()
                  

                  这显然会导致 shared_ptr 和 MyType * 的内存泄漏

                  【讨论】:

                  • 这会导致永久性的、不可恢复的内存泄漏。很难看到可以接受的情况。
                  • 是的,这就是重点。整个练习的重点是防止共享指针删除对象。唯一泄露的是共享指针,它只有几个字节,对程序完全没有意义
                  • 如果你这样做一次,它就没有任何意义。但是我们经常编写程序来一遍又一遍地做我们想做的事情。
                  • 在我的情况下,它是在加载时完成的。所有其他方法都会以相同的方式泄漏
                  猜你喜欢
                  • 2010-10-11
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2019-02-22
                  • 2014-09-12
                  • 1970-01-01
                  相关资源
                  最近更新 更多