La suppression fonctionne-t-elle avec des pointeurs vers la classe de base?

Devez-vous passer à supprimer le même pointeur renvoyé par new ou pouvez-vous lui passer un pointeur vers l’un des types de base de la classe? Par exemple:

class Base { public: virtual ~Base(); ... }; class IFoo { public: virtual ~IFoo() {} virtual void DoSomething() = 0; }; class Bar : public Base, public IFoo { public: virtual ~Bar(); void DoSomething(); ... }; Bar * pBar = new Bar; IFoo * pFoo = pBar; delete pFoo; 

Bien sûr, cela est grandement simplifié. Ce que je veux vraiment faire, c’est créer un conteneur plein de boost :: shared_ptr et le transmettre à un code qui le supprimera du conteneur lorsqu’il sera terminé. Ce code ne saura rien de l’implémentation de Bar ou Base et utilisera l’opérateur de suppression implicite dans le destructeur shared_ptr pour faire ce qu’il faut.

Est-ce que cela peut fonctionner? Mon intuition dit non, car les pointeurs n’auront pas la même adresse. D’un autre côté, un Dynamic_cast devrait fonctionner, donc quelque part le compilateur stocke suffisamment d’informations pour le comprendre.


Merci pour l’aide, à tous ceux qui ont répondu et commenté. Je connaissais déjà l’importance des destructeurs virtuels, comme le montre mon exemple; Après avoir vu la réponse, j’ai réfléchi un peu et réalisé que la raison d’ être d’un destructeur virtuel était ce scénario exact. Il fallait donc travailler. J’ai été frappé par l’absence d’un moyen visible de convertir le pointeur à l’original. Un peu plus de reflection m’a amené à croire qu’il y avait un moyen invisible, et j’ai théorisé que le destructeur renvoyait le vrai pointeur pour la suppression. L’examen du code compilé à partir de Microsoft VC ++ a confirmé mes soupçons lorsque j’ai vu cette ligne dans ~ Base:

 mov eax, DWORD PTR _this$[ebp] 

Le suivi de l’assembleur a révélé qu’il s’agissait du pointeur transmis à la fonction de suppression. Mystère résolu.

J’ai corrigé l’exemple pour append le destructeur virtuel à IFoo, c’était un simple contrôle. Merci encore à tous ceux qui l’ont souligné.

Oui, cela fonctionnera si et seulement si le destructeur de la classe de base est virtuel, ce que vous avez fait pour la classe de base mais pas pour la classe de base IFoo . Si le destructeur de classe de base est virtuel, lorsque vous appelez l’ operator delete sur le pointeur de classe de base, il utilise Dynamic dispatch pour déterminer comment supprimer l’object en recherchant le destructeur de classe dérivé dans la table de fonction virtuelle.

Dans le cas de l’inheritance multiple, cela ne fonctionnera que si la classe de base que vous supprimez possède un destructeur virtuel; les autres classes de base ne peuvent pas avoir de destructeur virtuel, mais seulement si vous n’essayez pas de supprimer des objects dérivés via ces autres pointeurs de classe de base.

Cela ne se rapporte pas à votre exemple, mais comme vous avez mentionné que vous êtes vraiment intéressé par le comportement de shared_ptr lors de la suppression de son object, vous pouvez être intéressé par le «deleter» de shared_ptr.

Si l’object détenu par shared_ptr nécessite un traitement spécial lors de la suppression, vous pouvez spécifier un ‘deleter’ pour tout shared_ptr<> . Le paramètre deleter ne fait pas partie du type, il s’agit d’un atsortingbut d’une instance shared_ptr<> , de sorte que votre conteneur d’objects shared_ptr<> pourrait avoir des objects avec différents parameters. Voici ce que disent les documents Boost sur le deleter shared_ptr<> :

Les désallocateurs personnalisés permettent à une fonction de fabrique de renvoyer un shared_ptr pour isoler l’utilisateur de sa stratégie d’allocation de mémoire. Le désallocateur ne faisant pas partie du type, la modification de la stratégie d’allocation ne rompt pas avec la compatibilité source ou binary et ne nécessite pas de recompilation du client. Par exemple, un désallocateur “no-op” est utile lors du renvoi d’un shared_ptr à un object alloué de manière statique, et d’autres variantes permettent d’utiliser un shared_ptr comme wrapper pour un autre pointeur intelligent, facilitant ainsi l’interopérabilité.

Ce serait plus propre si vous pouviez modifier IFoo pour avoir un destructeur virtuel puisque vous envisagez de supprimer des objects qui en sont des sous-classes via une référence ou un pointeur IFoo . Mais si vous êtes coincé avec un IFoo qui ne peut pas être corrigé, alors si vous voulez utiliser shared_ptr dans votre conteneur, mais que vous le pointez sur une Bar , vous pouvez créer l’instance shared_ptr avec un paramètre délesteur une Bar* alors l’opération de suppression. Les downcasts sont considérés comme une mauvaise forme, mais cette technique pourrait être quelque chose que vous pourriez utiliser dans un bind.