44 votes

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

Doit-on passer à delete le même pointeur que celui renvoyé par new, ou peut-on 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 rempli de boost::shared_ptr et le passer à un code qui le retirera du conteneur lorsqu'il aura terminé. Ce code ne saura rien de l'implémentation de Bar ou de Base, et comptera sur l'opérateur de suppression implicite dans le destructeur de shared_ptr pour faire ce qu'il faut.

Est-ce que cela peut fonctionner ? Mon intuition me dit que non, puisque les pointeurs n'auront pas la même adresse. D'un autre côté, un dynamic_cast<Bar*> devrait fonctionner, donc quelque part le compilateur stocke suffisamment d'informations pour le faire.


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'y ai réfléchi un peu, et j'ai réalisé que le raison entière pour un destructeur virtuel est ce scénario exact. Il fallait donc que cela fonctionne. J'ai été déconcerté par l'absence d'un moyen visible de reconvertir le pointeur vers l'original. Un peu plus de réflexion m'a amené à croire qu'il y avait un moyen invisible, et j'ai supposé que le destructeur retournait le vrai pointeur pour que delete le libère. L'examen du code compilé 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 ajouter le destructeur virtuel à IFoo, c'était un simple oubli. Merci encore à tous ceux qui l'ont signalé.

57voto

Adam Rosenfield Points 176408

Oui, ça va marcher, si et seulement si le destructeur de la classe de base est virtuel, ce que vous avez fait pour le destructeur de la classe de base. Base mais pas pour la classe de base IFoo classe de base. Si le destructeur de la classe de base est virtuel, alors lorsque vous appelez operator delete sur le pointeur de la classe de base, il utilise la répartition dynamique pour déterminer comment supprimer l'objet en recherchant le destructeur de la classe dérivée dans la table des fonctions virtuelles.

Dans votre cas d'héritage multiple, cela ne fonctionnera que si la classe de base que vous supprimez a un destructeur virtuel ; les autres classes de base peuvent ne pas avoir de destructeur virtuel, mais seulement si vous n'essayez pas de supprimer les objets dérivés via les pointeurs de ces autres classes de base.

3voto

Michael Burr Points 181287

Cela n'a rien à voir avec l'exemple que vous avez donné, mais puisque vous avez mentionné que vous êtes vraiment intéressé par shared_ptr Si vous souhaitez connaître le comportement de l'objet qu'il possède lorsqu'il le supprime, vous pouvez utiliser la méthode suivante shared_ptr Le "suppresseur" de l'entreprise.

Si l'objet appartenant à la shared_ptr doit faire l'objet d'un traitement spécial lors de sa suppression, vous pouvez spécifier un "suppresseur" pour tout type de fichier particulier. shared_ptr<> . Le suppresseur ne fait pas partie du type, c'est un attribut d'un shared_ptr<> de sorte que votre conteneur de shared_ptr<> pourrait avoir des objets avec des suppresseurs différents. Voici ce que dit la documentation de Boost à propos de la fonction shared_ptr<> deleter :

Les désalloueurs personnalisés permettent à une usine retournant un shared_ptr à isoler l'utilisateur de sa stratégie d'allocation stratégie d'allocation de la mémoire. Puisque le désalloueur ne fait pas partie du type, changer la stratégie d'allocation ne ne rompt pas la compatibilité source ou binaire et ne nécessite pas une recompilation du client. Par exemple, un désalloueur "no-op" est utile lorsque renvoyer un shared_ptr d'une manière statique alloué, et d'autres variantes permettent à un shared_ptr pour être utilisé comme un enveloppe pour un autre pointeur intelligent, facilitant ainsi l'interopérabilité.

Ce serait plus propre si vous pouviez modifier IFoo doit avoir un destructeur virtuel puisque vous prévoyez de supprimer les objets qui en sont des sous-classes par l'intermédiaire d'une méthode d'effacement des données. IFoo référence ou pointeur. Mais si vous êtes coincé avec un IFoo qui ne peut être corrigée, alors si vous voulez utiliser shared_ptr<IFoo> dans votre conteneur, mais faites en sorte qu'il pointe sur un fichier Bar vous pourriez créer le shared_ptr avec un suppresseur qui effectue un downcast vers une instance de Bar* puis effectue l'opération de suppression. Les downcasts sont considérés comme une mauvaise forme, mais cette technique pourrait être utilisée dans un bind.

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