web-dev-qa-db-fra.com

Downcasting shared_ptr <Base> vers shared_ptr <Derived>?

Mise à jour: le shared_ptr dans cet exemple est comme celui de Boost, mais il ne prend pas en charge shared_polymorphic_downcast (ou dynamic_pointer_cast ou static_pointer_cast d'ailleurs)!

J'essaie d'initialiser un pointeur partagé sur une classe dérivée sans perdre le nombre de références:

struct Base { };
struct Derived : public Base { };
shared_ptr<Base> base(new Base());
shared_ptr<Derived> derived;

// error: invalid conversion from 'Base* const' to 'Derived*'
derived = base;  

Jusqu'ici tout va bien. Je ne m'attendais pas à ce que C++ convertisse implicitement Base * en Derived *. Cependant, je veux la fonctionnalité exprimée par le code (c'est-à-dire maintenir le nombre de références tout en abaissant le pointeur de base). Ma première pensée a été de fournir un opérateur de cast dans Base afin qu'une conversion implicite en Derived puisse avoir lieu (pour les pédants: je vérifierais que le cast en bas est valide, ne vous inquiétez pas):

struct Base {
  operator Derived* ();
}
// ...
Base::operator Derived* () {
  return down_cast<Derived*>(this);
}

Eh bien, ça n'a pas aidé. Il semble que le compilateur ait complètement ignoré mon opérateur de transtypage. Des idées sur la façon dont je pourrais faire fonctionner l'affectation shared_ptr? Pour des points supplémentaires: quel type de type Base* const est? const Base* Je comprends, mais Base* const? À quoi const fait-il référence dans ce cas?

85
Lajos Nagy

Vous pouvez utiliser dynamic_pointer_cast. Il est soutenu par std::shared_ptr.

std::shared_ptr<Base> base (new Derived());
std::shared_ptr<Derived> derived =
               std::dynamic_pointer_cast<Derived> (base);

De plus, je ne recommande pas d'utiliser l'opérateur cast dans la classe de base. Un casting implicite comme celui-ci peut devenir la source de bugs et d'erreurs.

- Update: Si le type n'est pas polymorphe, std::static_pointer_cast peut être utilisé.

83
Massood Khaari

Je suppose que vous utilisez boost::shared_ptr... Je pense que vous voulez dynamic_pointer_cast ou shared_polymorphic_downcast.

Ceux-ci nécessitent cependant des types polymorphes.

quel type de type Base* const est? const Base* Je comprends, mais Base* const? À quoi const fait-il référence dans ce cas?

  • const Base * est un pointeur mutable sur une constante Base.
  • Base const * est un pointeur mutable sur une constante Base.
  • Base * const est un pointeur constant vers un Base mutable.
  • Base const * const est un pointeur constant vers une constante Base.

Voici un exemple minimal:

struct Base { virtual ~Base() { } };   // dynamic casts require polymorphic types
struct Derived : public Base { };

boost::shared_ptr<Base> base(new Base());
boost::shared_ptr<Derived> derived;
derived = boost::static_pointer_cast<Derived>(base);
derived = boost::dynamic_pointer_cast<Derived>(base);
derived = boost::shared_polymorphic_downcast<Derived>(base);

Je ne sais pas si c'était intentionnel que votre exemple crée une instance du type de base et la transforme, mais cela sert à illustrer la différence de manière intéressante.

Le static_pointer_cast va "le faire". Cela entraînera un comportement indéfini (un Derived* pointant sur la mémoire allouée et initialisée par Base) et provoquera probablement un plantage, ou pire. Le nombre de références sur base sera incrémenté.

Le dynamic_pointer_cast donnera un pointeur nul. Le nombre de références sur base sera inchangé.

Le shared_polymorphic_downcast aura le même résultat qu'une conversion statique, mais déclenchera une assertion, semblant plutôt réussir et conduire à un comportement indéfini. Le nombre de références sur base sera incrémenté.

Voir (lien mort) :

Parfois, il est un peu difficile de décider d'utiliser static_cast ou dynamic_cast, et vous souhaitez avoir un peu des deux mondes. Il est bien connu que dynamic_cast a un temps d'exécution, mais il est plus sûr, alors que static_cast n'a aucun temps, mais il peut échouer en silence. Comme ce serait bien si vous pouviez utiliser shared_dynamic_cast dans les versions de débogage et shared_static_cast dans les versions. Eh bien, une telle chose est déjà disponible et s'appelle shared_polymorphic_downcast.

46
Tim Sylvester

Si quelqu'un arrive ici avec boost :: shared_ptr ...

C'est ainsi que vous pouvez effectuer une conversion vers le Boost shared_ptr dérivé. En supposant que Derived hérite de Base.

boost::shared_ptr<Base> bS;
bS.reset(new Derived());

boost::shared_ptr<Derived> dS = boost::dynamic_pointer_cast<Derived,Base>(bS);
std::cout << "DerivedSPtr  is: " << std::boolalpha << (dS.get() != 0) << std::endl;

Assurez-vous que la classe/structure 'Base' a au moins une fonction virtuelle. Un destructeur virtuel fonctionne également.

4
Mitendra