c++ shared_ptr 释放所有权

c++ shared_ptr release ownership

我们都知道在C++中我们可以很容易地将unique_ptr转换为shared_ptr。但是如果我做了这样的转换呢:-

unique_ptr<X> u=make_unique<X>();    // X is some class
shared_ptr<X> s=move(u);      // this works of course

现在我想将 s 中指针的所有权转移回 u。遗憾的是没有 release() function in shared_ptr like in unique_ptr 否则我可能不会这样 :-

u.reset(s.release());

而且这也行不通:-

u.reset(s.get());

谁能建议我如何将 shared_ptr 转换为 unique_ptr 或至少释放 shared_ptr 拥有的指针?

你不应该那样做!

标准库并没有真正促进这样的移动,从 std::unique_ptrstd::shared_ptr 总是安全的(因为两者的语义)——但相反是危险的,因为可能比您想从中移动的特定 std::shared_ptr 拥有更多资源。


不,真的——不要那样做!

我可能应该再告诉你一次,但我假设你已经长大了,可以为自己的决定负责。


嘿,来吧..确定有破解方法吗?

如果您想拼凑出一个不会是 未定义行为 的解决方案,除非 std::unique_ptr 超出范围并且您仍然有 std::shared_ptrs 直接或间接地使用资源..你可能会得到这样的结果:

#include <memory>
namespace hack {
  struct conditional_deleter {
    void  disable () { _do_delete = false; }

    template<class T>
    void operator()(T* p) {
      if (_do_delete)
        delete p;
    }   

    bool _do_delete = true;
  };  
}
int main () {
  std::unique_ptr<int> up (new int (123));
  std::shared_ptr<int> sp (up.release (), hack::conditional_deleter {});
  std::shared_ptr<int> sb (sp);

  std::get_deleter<hack::conditional_deleter> (sp)->disable (); 
  std::unique_ptr<int> ub (sp.get ()); // see the name of this variable?
}                                      // can I get a ticket to UB-land, please?

WARNING

The above is far from recommended praxis, if you ever find yourself in a situation where you need such thing you should set fire to your workstation—probably your whole house—and work on a new program design.

好的,看了评论我现在意识到你想要的是 unique_ptrs 的 dynamic_pointer_cast 版本。

记住 unique_ptrs 很好......独一无二,这里是答案:

请注意,这个答案乍一看可能显得过于复杂,但我认为重要的是要记住 unique_ptrs 可以有自定义删除器。如果我们将 unique_ptr 动态转换为新的,删除器必须跟随,但如果不插入翻译垫片,传递给新 unique_ptr 的删除器的指针将是错误的类型。

此代码不仅动态地将所有权移动到新的指针类型,而且还连接了正确的反向指针转换,以便在移动到 unique_ptr 终于超出范围了。

#include <iostream>
#include <memory>

template<class Dest, class Source, class Deleter>
auto
make_proxy_deleter(std::unique_ptr<Source, Deleter>& source)
{
    return [original = source.get_deleter()](Dest* p) {
        original(dynamic_cast<Source*>(p));
    };
}

template<class Dest, class T, class Deleter>
auto
dynamic_cast_unique(std::unique_ptr<T, Deleter>&& source)
{
    auto proxy_deleter = make_proxy_deleter<Dest>(source);
    auto p = dynamic_cast<Dest*>(source.get());
    if (!p) {
        return std::unique_ptr<Dest, decltype(proxy_deleter)>(nullptr,
                                                              std::move(proxy_deleter));
        // or... throw std::invalid_argument("not convertible");
    }

    return std::unique_ptr<Dest, decltype(proxy_deleter)>(dynamic_cast<Dest*>(source.release()),
                                                          std::move(proxy_deleter));
}

struct A {
    virtual ~A() {};
};
struct B {
    virtual ~B() {};
};
struct C: A, B {};


using namespace std;

auto main() -> int
{
    auto pa = make_unique<C>();
    auto pb = dynamic_cast_unique<B>(std::move(pa));
    return 0;
}

正如大家提到的,您不能将 shared_ptr 转换为 unique_ptr,因为可能不止一个 shared_ptr 拥有该对象。因此 shared_ptr 没有 release() function。如果有一个并且你会在一个 shared_ptr 对象上使用 release() 当不止一个这样的对象共享资源时,那么其他 shared_ptr 指针是否会出现歧义是否拥有资源。

但是,因为您的实际问题是关于 unique_ptrstatic& dynamic casting(正如您在评论中提到的),所以我想补充一点,您实际上不需要使用 shared_ptr for casting unique_ptr & 在这里,我将向您展示如何非常简单地做到这一点:-

void dynamic_unique_cast (unique_ptr<Parent> &pa)
{
    unique_ptr<Child> pb;    
    Parent *p=pa.release();   // release ownership
    try
    {
       pb.reset(dynamic_cast<Child*>(p));  // perform casting
       if (pb==nullptr)
       throw runtime_error {"nullptr exception"};
       cout<<"dynamic_cast successful\n\n";
       pa.reset(pb.release());  // if casting is successful then `pb` returns ownership back to `pa`
    }
    catch (exception &e)
    {
       cout<<"dynamic_cast unsuccessful: "<<e.what()<<"\n\n";
       pa.reset(p);  // if casting fails then `p` returns ownership to `pa`
    }
}
void static_unique_cast (unique_ptr<Parent> &pa)
{
    unique_ptr<Child> pb;
    Parent *p=pa.release();
    try
    {
       pb.reset(static_cast<Child*>(p));
       if (pb==nullptr)
       throw runtime_error {"nullptr exception"};
       show(pb);
       cout<<"static_cast successful\n\n";
       pa.reset(pb.release());
    }
    catch (exception &e)
    {
       cout<<"static_cast unsuccessful: "<<e.what()<<"\n\n";
       pa.reset(p);
    }
}

以上代码运行良好,一定会解决您的问题。如果您有任何歧义,请随时发表评论。