【问题标题】:convert shared_ptr to auto_ptr?将 shared_ptr 转换为 auto_ptr?
【发布时间】:2023-03-06 21:53:01
【问题描述】:

我需要在我的代码中从 shared_ptr 获取 auto_ptr。我可以做反向操作 - 将 auto_ptr 转换为 shared_ptr 因为 shared_ptr 有这样的构造函数:

template<class Y> explicit shared_ptr(std::auto_ptr<Y> & r);

我可以将 shared_ptr 转换为 auto_ptr 吗?还是设计上不可能?

【问题讨论】:

    标签: c++ shared-ptr auto-ptr


    【解决方案1】:

    这在设计上是不可能的,因为对象可能与其他共享指针共享,因此将其“获取”到 auto_ptr 可能会导致删除引用的对象。

    出于同样的原因,shared_ptr 没有作为 auto_ptr 的“释放”成员函数。

    编辑:

    即使 shared_ptr 有某种“释放”方法或允许删除其引用 在不破坏对象的情况下,它不适用于以下情况(线程 A、B):

    A: { 
    A:     int count = sp.use_count();
      Context Switch
    B: shared_ptr<bar> my_sp = weak_sp.lock();
    B: // now use_count = 2 but A thinks it is 1
      Context Switch
    A:     auto_ptr<bar> ap;
    A:     if(count == 1) 
    A:      ap.reset(sp.release()); 
    A:      // actutally there is no sp.release but what if
    A:      ap->foo();
    A: }  // delete the object pointer by ap as it goes out of scope
      Context Switch
    B: my_sp->foo(); // Ooops - object is deleted!
    

    【讨论】:

    • shared_ptr 的“unique”方法原则上可以告诉您 shared_ptr 是否具有对该对象的唯一剩余引用,在这种情况下,转移到 auto_ptr 似乎更合理一些.但是没有任何支持的API(即发布方法)就没有办法。 (根据我的经验,使用 unique 或 count 的 shared_ptr 代码通常会出错)。
    • @timday unique 的问题是在使用weak_ptr 时会导致竞争条件。例如,如果当前计数为 1,但在其他线程中,某些弱指针用户创建 shared_ptr 它可能变为 2,您之前的检查将无效。
    • @Alf P. Steinbach 是的,这是正确的,因为在检查“uniquness”之后和实际发布之前,weak_ptr 转换为 shared_ptr 的危险。
    • @Artyom:你说这是不可能的,我已经证明在某种情况下它是可能的。 “不可能”!=“可能”。抱歉,您的回答完全错误。
    • @Alf:有不同的shared_ptr 实现。答案是模糊的,更糟糕​​的是,没有错。例如,在 C++0x 中确实是不可能的。
    【解决方案2】:

    一个共享指针可以被很多东西共享,你不能只是从它们那里拿走它。这由Artyompeoro 详细阐述。

    一种方法是创建一个临时的auto_ptr,并将其从作用域末尾的指针处理中释放出来。 dalle 概述了第一种方法,但是这种方法缺乏异常安全性(可能会意外删除),并且它不能保护您不意外地将其传递给将要转移所有权的函数(删除不在我们手中) .

    不过,我们可以制作自己的包装器来避免这种情况:

    template <typename T>
    class auto_ptr_facade
    {
    public:   
        auto_ptr_facade(shared_ptr<T> ptr) :
        mPtr(ptr),
        mAuto(ptr.get())
        {}
    
        ~auto_ptr_facade()
        {
            // doesn't actually have ownership
            mAuto.release();
        }
    
        // only expose as const, cannot be transferred
        const auto_ptr<T>& get() const
        {
             return mAuto;
        }
    
        operator const auto_ptr<T>&() const
        {
             return get();
        }
    
    private:
        auto_ptr_facade(const auto_ptr_facade&);
        auto_ptr_facade& operator=(const auto_ptr_facade&);
    
        shared_ptr<T> mPtr;
        auto_ptr<T> mAuto;
    };
    

    现在您可以在作用域内将shared_ptr 视为const auto_ptr

    template <typename T>
    void foo(shared_ptr<T> ptr)
    {
        auto_ptr_facade<T> a(ptr);
    
        // use a
    }
    

    【讨论】:

    • @GMan: 但是,如果它过期了,那么指针已经被 ptr.reset() 删除并且结果变成了一个悬空指针
    • 在几乎任何有用的情况下,ptr 都是作为参数传递的 shared_ptr 的副本:try_make_auto_ptr(my_local_ptr),因此它不会过期。
    • @ybungalobill:哦,呵呵,哈哈。困。 :) 我已经扩展了 dalle 的答案。
    • -1 在当前代码中ptr是按值传递的。这几乎可以确保它不是唯一的参考。此外,使用weak_ptr 会不必要地降低代码效率。 Methinks 的原作者是 Java 人。 shared_ptr::use_count 存在于 boost::shared_ptr 和 C++0x std::shared_ptr 中。最后,reset 会倾向于破坏引用的对象。哦哦...
    • @Gman:好的,全新的方法。我删除了我的反对票! :-) 顺便说一句,如果你知道shard_ptr 的删除函数的类型,那么你真的可以拿走所有权。但是通过公共接口,只有在创建时指定了自定义销毁函数时才有可能。我记得我曾为保留get_deleter 的东西而奋力争辩。但是,如果默认删除函数的类型可用,那就太好了!干杯,
    【解决方案3】:

    通常这是一个坏主意,因为 auto_ptrshared_ptr 都拥有您的指针的所有权(根据不同的政策,他们会关心销毁它和其他东西)。

    让两个不同的对象持有同一个指针的所有权可能会导致运行时错误,除非您出于某些非常好的(以及奇怪!)原因这样做。

    【讨论】:

      【解决方案4】:

      假设您想将所有权shared_ptr 转移到auto_ptr,这只有在

      • shared_ptr 的引用计数为 1,并且
      • shared_ptr 最初是使用自定义删除函数创建的,并且
      • 您知道删除函数的类型。

      鉴于此,方法如下:

      #include <iostream>
      #include <boost/shared_ptr.hpp>     // boost::shared_ptr
      #include <memory>                   // std::auto_ptr
      
      typedef boost::shared_ptr<int>  IntSharedPtr;
      typedef std::auto_ptr<int>      IntAutoPtr;
      
      template< class Type >
      void myCustomDeleter( Type* p )
      {
          delete p;
      }
      
      IntSharedPtr newSharedInt()
      {
          return IntSharedPtr( new int( 42 ), &myCustomDeleter<int> );
      }
      
      IntAutoPtr makeAutoFrom( IntSharedPtr& sp )
      {
          struct Dummy
          {
              static void deleter( int* ) {}
          };
      
          typedef void (*DeleterFunc)( int* );
      
          if( sp.use_count() > 1 ) { return IntAutoPtr( 0 ); }
          DeleterFunc*    d   = boost::get_deleter<DeleterFunc>( sp );
      
          if( d == 0 ) { return IntAutoPtr( 0 ); }
      
          int* const  p   = sp.get();
          *d = &Dummy::deleter;
          sp.reset();
          return IntAutoPtr( p );
      }
      
      template< class T >
      T& refTo( T const& r ) { return const_cast< T& >( r ); }
      
      int main()
      {
          IntAutoPtr  p( makeAutoFrom( refTo( newSharedInt() ) ) );
      
          std::cout << (p.get() == 0? "Failed" : "Worked" ) << std::endl;
      }
      

      注意:这种技术不是线程安全的。

      干杯,

      【讨论】:

      • -1 它不是线程安全的,而且非常危险,更重要的是它不能成为线程安全的。我强烈反对使用这种“技巧”。
      • @Artyom:这是 OP 问题的答案。您不应该仅仅因为您不喜欢 X 而对告诉您存在 X 的答案(当 OP 询问 X 时)投反对票。然后改为对问题投反对票。
      • 作者明确询问这是否是设计,是的,是设计使然。几乎所有事情都可以解决,但这并不能使这种方法合理。
      • 还有 shared_ptr 被设计成线程安全的,这种访问违反了它的属性。
      • @Artyom:OP 询问它是否“设计上不可能”。您回答“设计不可能”,这是不正确的。这就是为什么我不赞成您的回答:您的回答是不正确。我展示了设计存在的可能性。哪个是正确答案。是的,get_deleter 是设计的。它还有其他用途。
      【解决方案5】:

      您不应该这样做,因为auto_ptr 拥有指针的所有权。

      但您可以这样做,但请确保在超出范围之前致电release

      void foo(shared_ptr<Y> s)
      {
          auto_ptr<Y> a(s.get());
      
          // use a
      
          a.release();
      }
      

      编辑:上述解决方案并非异常安全。以下应该可以工作,将 guard 类与 const auto_ptr 不能被复制的保证结合起来:

      void bar(const auto_ptr<Y>& p);
      
      struct as_const_auto_ptr
      {
          explicit as_const_auto_ptr(const shared_ptr<Y>& p) : p(p), a(p.get()) {}
          ~as_const_auto_ptr() {a.release();}
          operator const auto_ptr<Y>&() {return a;}
          const shared_ptr<Y> p;
          auto_ptr<Y> a;
      };
      
      void foo(shared_ptr<Y> s)
      {
          as_const_auto_ptr a(s);
      
          // use a.
          bar(a);
      }
      

      【讨论】:

      • 如果在// use a期间抛出异常?
      • @ybungalobill:如果我不小心将它按值传递给函数? :) 这个解决方案太容易搞砸了。
      • @GMan:这不是异常安全的,但正如@ybungalobill 所说,使用保护对象。
      • @GMan:好吧,恢复我的投票。虽然它做了问题中提出的问题,但它肯定没用!
      • @GMan:但正如我所说,你不应该这样做,但仍有可能。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-07-22
      • 2017-08-06
      • 2010-11-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多