122 votes

Downcasting shared_ptr<Base> vers shared_ptr<Derived> ?

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

J'essaie d'initialiser un pointeur partagé vers une classe dérivée sans perdre le compte des 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'à présent, tout va bien. Je ne m'attendais pas à ce que C++ convertisse implicitement Base* en Dérivé*. Cependant, je veux la fonctionnalité exprimée par le code (c'est-à-dire maintenir le nombre de références tout en convertissant le pointeur de base). Ma première idée était de fournir un opérateur de cast dans la base pour qu'une conversion implicite en dérivé puisse avoir lieu (pour les pédants : je vérifierais que le down cast 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 typecast. Avez-vous une idée de la façon dont je pourrais faire fonctionner l'affectation de shared_ptr ? Pour les points supplémentaires : quel type de Base* const est ? const Base* Je comprends, mais Base* const ? Que fait const dans ce cas ?

0 votes

Pourquoi avez-vous besoin d'un shared_ptr<Derived>, au lieu d'un shared_ptr<Base> ?

4 votes

Parce que je veux accéder à une fonctionnalité dans Derived qui n'est pas dans Base, sans cloner l'objet (je veux un seul objet, référencé par deux pointeurs partagés). Au fait, pourquoi les opérateurs de cast ne fonctionnent-ils pas ?

144voto

Massood Khaari Points 358

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

Documentation : https://en.cppreference.com/w/cpp/memory/shared_ptr/pointer_cast

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

-Mise à jour : Si le type n'est pas polymorphe, std::static_pointer_cast peuvent être utilisés.

5 votes

Je n'ai pas compris dès la première ligne qu'il n'utilise pas std::shared_ptr . Mais d'après les commentaires de la première réponse, j'ai déduit qu'il n'utilisait pas boost, donc il utilise peut-être std::shared_ptr .

0 votes

OK. Désolé. Il aurait dû préciser qu'il utilisait une implémentation personnalisée.

50voto

Tim Sylvester Points 14047

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

Cependant, ceux-ci nécessitent des types polymorphes.

quel genre de type Base* const est ? const Base* Je comprends, mais Base* const ? Que fait const dans ce cas ?

  • const Base * est un pointeur mutable vers une constante Base .
  • Base const * est un pointeur mutable vers une constante Base .
  • Base * const est un pointeur constant vers un objet mutable Base .
  • 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 suis pas sûr qu'il était intentionnel que votre exemple crée une instance du type de base et la transforme, mais cela permet d'illustrer la différence de façon intéressante.

El static_pointer_cast va "juste le faire". Il en résultera un comportement indéfini (une Derived* pointant vers la mémoire allouée pour et initialisée par Base ) et provoquera probablement un crash, ou pire. Le nombre de références sur base sera incrémenté.

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

El shared_polymorphic_downcast aura le même résultat qu'un cast statique, mais déclenchera une assertion, plutôt que de sembler réussir et de 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 si l'on doit utiliser static_cast o dynamic_cast et tu souhaites avoir un peu des deux mondes. Il est bien connu que la méthode dynamic_cast a une surcharge d'exécution, mais elle est plus sûre, alors que la méthode static_cast n'a pas de surcharge du tout, mais elle peut échouer silencieusement. Ce serait bien si vous pouviez utiliser shared_dynamic_cast dans les constructions de débogage, et shared_static_cast dans les versions de lancement. Eh bien, une telle chose est déjà disponible et s'appelle shared_polymorphic_downcast .

0 votes

Malheureusement, votre solution dépend de la fonctionnalité Boost qui a été délibérément exclue de l'implémentation particulière de shared_ptr que nous utilisons (ne demandez pas pourquoi). Quant à l'explication de const, elle a beaucoup plus de sens maintenant.

3 votes

A défaut de mettre en œuvre l'autre shared_ptr (en prenant static_cast_tag y dynamic_cast_tag ), il n'y a pas grand-chose à faire. Tout ce que vous faites en dehors shared_ptr ne sera pas en mesure de gérer le refcount. -- Dans une conception OO "parfaite", vous pouvez toujours utiliser le type de base, et ne jamais avoir besoin de savoir ni de vous soucier de ce qu'est le type dérivé, car toutes ses fonctionnalités sont exposées à travers les interfaces de la classe de base. Peut-être devez-vous simplement repenser à la raison pour laquelle vous avez besoin d'effectuer un down-cast en premier lieu.

1 votes

@Tim Sylvester : mais, C++ n'est pas un langage OO "parfait" ! :-) Les down-casts ont leur place dans un langage OO non parfait.

5voto

mitendra Points 156

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

C'est ainsi que vous pouvez faire un downcast vers le shared_ptr dérivé de Boost. 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 "de base" possède au moins une fonction virtuelle. Un destructeur virtuel fonctionne également.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X