如何使用unique_ptr执行dynamic_cast?

时间:2014-10-15 08:01:53

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

我有一个类层次结构如下:

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()));

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

4 个答案:

答案 0 :(得分:19)

更新

问题已得到澄清:

  

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

在这种情况下,解决方案就是:

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

最简单的方法似乎是:

#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<>不存在的原因可能是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));

答案 1 :(得分:9)

除非您想转移std::unique_ptr<T>的所有权,否则您的函数应该使用指针或引用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));

答案 2 :(得分:1)

这是增强的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 );
}

答案 3 :(得分:-1)

只需使用std::unique_ptr<>::get()方法获取存储的指针:

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

如果shared_from_this()有原型:

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