【问题标题】:How to perform a dynamic_cast with a unique_ptr?如何使用 unique_ptr 执行 dynamic_cast?
【发布时间】:2014-12-10 05:28:21
【问题描述】:

我的类层次结构如下:

class BaseSession : public boost::enable_shared_from_this<BaseSession>
class DerivedSessionA : public BaseSession
class DerivedSessionB : public BaseSession

在派生类函数中,我经常这样调用函数:

Func(boost::dynamic_pointer_cast<DerivedSessionA>(shared_from_this()));

由于我与shared_ptr 合作管理会话,因此工作正常。最近,我发现我对shared_ptr 的使用对于这种情况并不是最佳的。这是因为这些会话是为每个客户端维护一个套接字的单例对象。如果重新连接套接字,会话副本将成为僵尸。

作为解决方法,我开始通过引用而不是副本传递shared_ptr。这解决了僵尸问题。

理想情况下,我觉得我应该使用unique_ptr 来存储会话,然后将引用传递给其他函数。这打开了一整罐蠕虫。

如何将基类unique_ptr 对象转换为派生类unique_ptr 对象?以下行的unique_ptr 版本是什么?

Func(boost::dynamic_pointer_cast<DerivedSessionA>(shared_from_this()));

我只想要一个会话对象的副本,其他的都应该是参考。

【问题讨论】:

  • 如果动态转换失败,你的指向对象会发生什么?是应该删除它还是只希望在演员成功时移动而不是删除?
  • 如果 dynamic_cast 失败,Func 得到一个空对象并取消操作。我不想搬家,主人应该保持原样。
  • 我怀疑我正在寻找一个 weak_ptr 到 unique_ptr 对象。除了回到C指针,还有其他选择吗?
  • @SharathKShetty 简短回答:不。 unique_ptr 表示所有权。弱指针需要包含锁定和引用计数的观察逻辑。这是有代价的,这就是为什么如果你愿意的话,你需要使用shared_ptr。好消息是你也可以使用std::dynamic_pointer_cast&lt;&gt; weak_ptr)。每个人都赢了。
  • 我同意sehe。创建一个指向另一个智能指针已经拥有的资源的 unique_ptr 而不在两个容器之间移动资源是错误的。 weak_ptr 没有指向的资源的所有权,而是一个弱引用:所以从 weak_ptr 到 unique_ptr 的任何操作都是不可行的或非常不鼓励

标签: c++ boost smart-pointers unique-ptr dynamic-cast


【解决方案1】:

这是 boost 的 dynamic_pointer_cast。这个想法很简单(但忽略删除器)。

//dynamic_pointer_cast overload for std::unique_ptr
template<class T, class U> std::unique_ptr<T> dynamic_pointer_cast( std::unique_ptr<U> && r ) BOOST_SP_NOEXCEPT
{
    (void) dynamic_cast< T* >( static_cast< U* >( 0 ) );

    BOOST_STATIC_ASSERT_MSG( boost::has_virtual_destructor<T>::value, "The target of dynamic_pointer_cast must have a virtual destructor." );

    T * p = dynamic_cast<T*>( r.get() );
    if( p ) r.release();
    return std::unique_ptr<T>( p );
}

【讨论】:

    【解决方案2】:

    更新

    问题已经澄清:

    对不起,我不清楚。我希望所有权保留给原始所有者,被调用的函数应该只引用它,而不是所有权。不是为同一个对象寻找两个智能指针。

    在这种情况下,解决方案很简单:

    dynamic_cast<B&>(*my_unique_ptr)
    

    完成。如果演员表不成功,它会抛出。


    铸造shared_ptr

    对于shared_ptr,有std::dynamic_pointer_cast&lt;&gt; (http://en.cppreference.com/w/cpp/memory/shared_ptr/pointer_cast)

    铸造unique_ptr

    最简单的方法似乎是:

    #include <memory>
    
    struct A { virtual ~A() = default; };
    struct B : A { };
    
    int main()
    {
        std::unique_ptr<A> pa(new B);
    
        std::unique_ptr<B> pb(dynamic_cast<B*>(pa.release())); // DO NOT DO THIS
    }
    

    正如评论者正确指出的那样,如果转换失败,这可能会泄漏对象。这不是很有帮助。

    dynamic_unique_ptr_cast&lt;&gt; 不存在的一个原因可能是unique_ptr 类型不会删除删除器。为目标指针类型选择适当的删除可能很难/不可能。

    但是,对于简单的情况,您可以使用以下内容:

    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<B>(std::move(pa));
    

    【讨论】:

    • 我想避免移动会话对象的所有权。我想在动态转换之后通过引用传递 unique_ptr。
    • 谢谢@sehe。这是对 Jarad42 解决方案的一个很好的转折。 :-)
    • 是的,在过去的两天里,这篇帖子引起了很多噪音。上次我试图尽可能地保留价值,但这个讨论已经进行了太长时间,而且似乎不会很快取得成果。要么拿它去聊天,要么你们中的一个人可以就这个问题提出一个新问题,让冷静的第三者找出正确的答案。
    • 我只能通过完全删除Deleter 模板参数来使其工作,因为default_delete&lt;From&gt; 不会转换为default_delete&lt;To&gt;。但这就是我感兴趣的“简单案例”。
    • 感谢@Danra,这实际上可能会为之前遇到类似情况的其他人提供启发,而我并不知道。如果您有一个最小的示例,您可以将其添加到答案中以防万一
    【解决方案3】:

    除非您想转移您的std::unique_ptr&lt;T&gt; 的所有权,否则您的函数应该采用指向T 的指针或引用。

    所以Func 的签名应该类似于Func(DerivedSessionA*)

    然后你的电话可能看起来像:

    std::unique_ptr<BaseSession> ptr; // Initialize it with correct value
    
    Func(dynamic_cast<DerivedSessionA*>(ptr.get()));
    

    或者您似乎直接从BaseSession 中的方法调用它:

    Func(dynamic_cast<DerivedSessionA*>(this));
    

    【讨论】:

    • 答案没有说明如果演员阵容失败该怎么办。
    • dynamic_cast 返回 nullptr 如果动态类型不是真正的 DerivedSessionA*(或子类型)。
    • @ChristopherPisz 如果转换失败该怎么办取决于其他上下文,并且与“如果来自原始指针的dynamic_cast 失败该怎么办”
    • 我认为在转换前后指出谁拥有指针也很重要,并提到 unique_ptr::release 与 get。
    • 如果我想转让所有权怎么办?
    【解决方案4】:

    只需使用std::unique_ptr&lt;&gt;::get()方法获取存储的指针:

    Func(dynamic_cast<DerivedSessionA*>(shared_from_this().get()))
    

    如果shared_from_this() 有那个原型:

    std::unique_ptr<BaseSession>& shared_from_this();
    

    【讨论】:

    • 但是 shared_from_this() 总是返回一个 shared_ptr,而不是 unique_ptr。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-05-04
    • 2019-09-24
    • 1970-01-01
    • 1970-01-01
    • 2013-06-20
    • 2018-12-11
    • 1970-01-01
    相关资源
    最近更新 更多