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.
À 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));
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.
shared_ptr
Pour shared_ptr
il y a std::dynamic_pointer_cast<>
( http://en.cppreference.com/w/cpp/memory/shared_ptr/pointer_cast )
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));
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 );
}