web-dev-qa-db-fra.com

Comment faire un dynamic_cast avec un unique_ptr?

J'ai une hiérarchie de classes comme suit:

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

Dans les fonctions de classe dérivées, j'appelle régulièrement des fonctions comme celle-ci:

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

Depuis que je travaillais avec shared_ptr pour gérer les sessions, cela fonctionnait bien. Récemment, j'ai découvert que mon utilisation de shared_ptr n'est pas optimal dans ce cas. En effet, ces sessions sont des objets singleton qui conservent un socket par client. Si le socket est reconnecté, les copies de session devenaient des zombies.

Pour contourner ce problème, j'ai commencé à passer shared_ptr par référence plutôt que par copies. Cela a résolu le problème des zombies.

Idéalement, je pensais que je devrais utiliser unique_ptr pour stocker la session, puis transmettre des références à d'autres fonctions. Cela a ouvert toute une boîte de vers.

Comment convertir une classe de base unique_ptr objet à la classe dérivée unique_ptr objet? Quel est le unique_ptr version de la ligne suivante?

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

Je veux juste une copie de l'objet session, tout le reste devrait être une référence.

20
Sharath

À moins que vous ne souhaitiez transférer la propriété de votre std::unique_ptr<T>, Votre fonction doit prendre un pointeur ou une référence à T.

La signature de Func devrait donc ressembler à Func(DerivedSessionA*)

puis votre appel peut ressembler à:

std::unique_ptr<BaseSession> ptr; // Initialize it with correct value

Func(dynamic_cast<DerivedSessionA*>(ptr.get()));

Ou comme vous semblez l'appeler directement à partir d'une méthode dans BaseSession:

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

Mise à jour

La question a été clarifiée:

désolé je n'étais pas clair. Je veux que la propriété reste avec le propriétaire d'origine, la fonction appelée doit uniquement y faire référence, pas la propriété. Ne cherche pas deux pointeurs intelligents pour le même objet.

Dans ce cas, la solution est simplement:

dynamic_cast<B&>(*my_unique_ptr)

Terminé . Il lance si le lancer ne réussit pas.


Casting shared_ptr

Pour shared_ptr il y a std::dynamic_pointer_cast<> ( http://en.cppreference.com/w/cpp/memory/shared_ptr/pointer_cast )

Casting unique_ptr

La manière la plus simple semblerait:

#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
}

Comme le commentateur le souligne à juste titre, cela peut faire fuir l'objet si la conversion a échoué. Ce n'est pas très utile.

Une raison pour laquelle le dynamic_unique_ptr_cast<> n'existe pas peut-être que le unique_ptr type n'efface pas le suppresseur. Il peut être difficile/impossible de choisir une suppression appropriée pour le type de pointeur cible.

Cependant, pour des cas simples, vous pouvez utiliser quelque chose comme ceci:

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

Il s'agit de dynamic_pointer_cast de boost. L'idée est assez simple (mais ignorez le deleter).

//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