How to do perform a dynamic_cast with a unique_ptr?
我有一个类层次结构,如下所示:
1 2 3
| class BaseSession : public boost::enable_shared_from_this<BaseSession>
class DerivedSessionA : public BaseSession
class DerivedSessionB : public BaseSession |
在派生类函数中,我经常调用如下函数:
1
| Func(boost::dynamic_pointer_cast<DerivedSessionA>(shared_from_this())); |
因为我和shared_ptr合作管理会议,所以工作很顺利。最近,我发现我使用shared_ptr并不适合这个案例。这是因为这些会话是单例对象,每个客户端维护一个套接字。如果重新连接套接字,会话副本将变成僵尸。
为了解决这个问题,我开始通过引用而不是副本来传递shared_ptr。这解决了僵尸问题。
理想情况下,我觉得应该使用unique_ptr来存储会话,然后传递对其他函数的引用。打开了一整罐虫子。
如何将基类unique_ptr对象强制转换为派生类unique_ptr对象?以下行的unique_ptr版本是什么?
1
| Func(boost::dynamic_pointer_cast<DerivedSessionA>(shared_from_this())); |
我只需要会话对象的一个副本,其他所有内容都应该是引用。
- 如果动态强制转换失败,您的指向对象会发生什么情况?是应该删除它,还是只有在强制转换成功且从不删除时才希望移动?
- 如果动态_cast失败,func将得到一个空对象,并取消该操作。我不想搬走,主人应该保持原样。
- 我怀疑我在寻找一个弱的唯一的对象。除了回到C指针,还有其他选择吗?
- @sharathkshetty简短回答:不,unique_ptr表示所有权。弱指针需要包含锁定和重新计数的观察逻辑。这是有代价的,所以如果你这么想的话,你需要使用shared_ptr。好消息是你也可以使用std::dynamic_pointer_cast<>和weak_ptr。人人都赢。
- 我同意塞赫的观点。在两个容器之间不移动资源的情况下,创建一个指向已由另一个智能指针拥有的资源的唯一指针是错误的。弱指针不拥有指向的资源的所有权,但具有弱引用:因此,从弱指针到唯一指针的任何操作都是不可行的或非常不鼓励的。
- @Stefanobuora:这个资源将只由unqiue-ptr拥有,我想去掉共享的资源。看来Jarod42提供的解决方案是我的最佳选择。
更新
问题已经澄清:
sorry I was not clear. I want the ownership to remain with original owner, the called function should only get reference to it, not ownership. Not looking for two smart pointer for the same object.
在这种情况下,解决方案很简单:
1
| dynamic_cast<B&>(*my_unique_ptr) |
完成。如果施法不成功,它就会抛出。
铸造
shared_ptr对于shared_ptr有std::dynamic_pointer_cast<>(http://en.cppreference.com/w/cpp/memory/shared-ptr/pointer-cast)
铸造
unique_ptr。
最简单的方法是:
1 2 3 4 5 6 7 8 9 10 11
| #include <memory>
struct A { virtual ~A() = default; };
struct B : A { };
int main()
{
std::unique_ptr<A> pa(new B);
std::unique_ptr pb(dynamic_cast<B*>(pa.release())); // DO NOT DO THIS
} |
正如评论者正确指出的那样,如果转换失败,这可能会泄漏对象。那不是很有帮助。
dynamic_unique_ptr_cast<>不存在的一个原因可能是unique_ptr类型没有删除删除程序。可能很难/不可能为目标指针类型选择适当的删除。
但是,对于简单的情况,您可以使用如下内容:
1 2 3 4 5 6 7 8 9 10 11 12 13
| template <typename To, typename From, typename Deleter>
std::unique_ptr<To, Deleter> dynamic_unique_cast(std::unique_ptr<From, Deleter>&& p) {
if (To* cast = dynamic_cast<To*>(p.get()))
{
std::unique_ptr<To, Deleter> result(cast, std::move(p.get_deleter()));
p.release();
return result;
}
return std::unique_ptr<To, Deleter>(nullptr); // or throw std::bad_cast() if you prefer
}
auto pb = dynamic_unique_cast(std::move(pa)); |
- 我希望避免移动会话对象的所有权。我想在动态强制转换之后通过引用传递唯一的指针。
- @零意义的沙拉特克西蒂。如果要共享所有权,请使用共享指针,甚至使用原始指针(dynamic_cast(p.get()))。为同一个对象创建两个不同的智能指针总是一种糟糕的设计味道,并且会带来麻烦。unique_ptr正是为了转移所有权。如果你不需要它,就用别的东西。
- 对不起,我不清楚。我希望所有权仍属于原始所有者,被调用的函数应该只引用它,而不是所有权。不为同一对象查找两个智能指针。我在我问题的最后一行提到过这一点。
- @在这种情况下,sharathkshetty:dynamic_cast(*my_unique_ptr)完成。如果施法不成功,它就会抛出。
- 谢谢。这是贾拉德42解决方案的一个很好的转折点。-)
- @我不明白这是怎么回事。只是做你想做的。没有不必要的并发症。我把它添加到我的答案中,只是为了完整。
- @沙拉特,我明白你为什么说这是一个转折点——他们的意图相似。当然,不同之处在于(如前所述)强制转换引用版本将引发(std::bad_cast),但强制转换指针版本将不会(可以为空)。我认为两者都是合适的,这取决于你想如何处理一个失败的演员。
- 它在2010年起作用,但在Visual Studio 2015中会出现编译器错误C2440。
- 你做错了什么(猜测:你的类型根本不是多态的,或者类型不是相关的,或者是一个简单的错误?)
- 我从未要求你解决我的问题。我通过去掉你的代码并用常识替换它来解决我的问题。为了测试您的代码片段,我复制了将您的代码粘贴到VC140中一个闪亮的新项目中,但它没有编译。如果您查找错误,则错误C2440肯定适用。
- 是的,在过去的两天里,有很多噪音来自这篇文章。上一次我试图尽可能地保持价值,但这场讨论已经进行得太久了,似乎不会很快见效。或者带着它聊天,或者你们中的一个可以问一个关于这个问题的新问题,让一个冷静的第三方找出正确的答案。
除非您想转移您的std::unique_ptr的所有权,否则您的函数应该使用指向T的指针或引用。
因此,Func的签名应该类似于Func(DerivedSessionA*)的签名。
然后你的电话看起来像:
1 2 3
| std::unique_ptr<BaseSession> ptr; // Initialize it with correct value
Func(dynamic_cast<DerivedSessionA*>(ptr.get())); |
或者您似乎直接从BaseSession中的方法调用它:
1
| Func(dynamic_cast<DerivedSessionA*>(this)); |
- 答案并不能解释如果演员阵容失败该怎么办。
- 如果动态类型的EDOCX1(或子类型)不是实际的EDOCX1(或子类型),则dynamic_cast返回nullptr。
只需使用std::unique_ptr<>::get()方法获取存储的指针:
1
| Func(dynamic_cast<DerivedSessionA*>(shared_from_this().get())) |
如果shared_from_this()有这个原型:
1
| std::unique_ptr<BaseSession>& shared_from_this(); |
- 但shared_from_this()始终返回共享的_ptr,而不是唯一的_ptr。