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_ptr
到 std::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_ptr
s 的 dynamic_pointer_cast
版本。
记住 unique_ptrs 很好......独一无二,这里是答案:
请注意,这个答案乍一看可能显得过于复杂,但我认为重要的是要记住 unique_ptr
s 可以有自定义删除器。如果我们将 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_ptr
的 static& 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);
}
}
以上代码运行良好,一定会解决您的问题。如果您有任何歧义,请随时发表评论。
我们都知道在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_ptr
到 std::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_ptr
s 的 dynamic_pointer_cast
版本。
记住 unique_ptrs 很好......独一无二,这里是答案:
请注意,这个答案乍一看可能显得过于复杂,但我认为重要的是要记住 unique_ptr
s 可以有自定义删除器。如果我们将 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_ptr
的 static& 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);
}
}
以上代码运行良好,一定会解决您的问题。如果您有任何歧义,请随时发表评论。