web-dev-qa-db-ja.com

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の使用がこの場合に最適ではないことを発見しました。これは、これらのセッションがクライアントごとに1つのソケットを保持するシングルトンオブジェクトであるためです。ソケットが再接続されると、セッションコピーがゾンビになるために使用されます。

回避策として、コピーではなく参照によってshared_ptrを渡し始めました。これにより、ゾンビの問題が解決しました。

理想的には、unique_ptrを使用してセッションを保存し、他の関数への参照を渡す必要があると感じました。それはワームの缶全体を開きました。

基本クラスunique_ptrオブジェクトを派生クラスunique_ptrオブジェクトにキャストするにはどうすればよいですか?次の行のunique_ptrバージョンとは何ですか?

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

セッションオブジェクトのコピーが1つだけ必要です。それ以外はすべて参照する必要があります。

20
Sharath

_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));
_
10
Jarod42

更新

質問が明確になりました:

申し訳ありませんが明確ではありませんでした。所有権は元の所有者のままにしておきたいのですが、呼び出された関数は所有権ではなく、その参照のみを取得する必要があります。同じオブジェクトの2つのスマートポインターを探していません。

その場合の解決策は次のとおりです。

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
}

コメント者が正しく指摘しているように、変換が失敗した場合、オブジェクトがリークする可能性があります。それはnot非常に有用です。

dynamic_unique_ptr_cast<>が存在しない場合、unique_ptr typeは削除者を消去しません。ターゲットポインタータイプに適切な削除を選択するのは難しい/不可能な場合があります。

ただし、単純な場合には、次のようなものを使用できます。

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));
24
sehe

これは、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 );
}
0
xinnjie