Je sais que c’est une bonne pratique de déclarer des destructeurs virtuels pour les classes de base en C++, mais est-il toujours important de déclarer des destructeurs virtual
même pour des classes abstraites fonctionnant comme des interfaces? Veuillez donner quelques raisons et exemples.
C'est encore plus important pour une interface. Tout utilisateur de votre classe aura probablement un pointeur sur l'interface, pas un pointeur sur l'implémentation concrète. Lorsqu'ils viennent pour le supprimer, si le destructeur n'est pas virtuel, ils appellent le destructeur de l'interface (ou la valeur par défaut fournie par le compilateur, si vous n'en avez pas spécifié un), et non le destructeur de la classe dérivée. Fuite de mémoire instantanée.
Par exemple
class Interface
{
virtual void doSomething() = 0;
};
class Derived : public Interface
{
Derived();
~Derived()
{
// Do some important cleanup...
}
};
void myFunc(void)
{
Interface* p = new Derived();
// The behaviour of the next line is undefined. It probably
// calls Interface::~Interface, not Derived::~Derived
delete p;
}
La réponse à votre question est souvent, mais pas toujours. Si votre classe abstraite interdit aux clients d'appeler delete sur un pointeur vers celle-ci (ou si cela est indiqué dans sa documentation), vous êtes libre de ne pas déclarer un destructeur virtuel.
Vous pouvez interdire aux clients d'appeler delete sur un pointeur vers celui-ci en protégeant son destructeur. En procédant ainsi, il est parfaitement sûr et raisonnable d’omettre un destructeur virtuel.
Vous finirez par vous retrouver sans table de méthode virtuelle et signalerez à vos clients votre intention de la rendre non supprimable par un pointeur sur celle-ci. Vous avez donc des raisons de ne pas la déclarer virtuelle dans ces cas.
[Voir le point 4 de cet article: http://www.gotw.ca/publications/mill18.htm ]
J'ai décidé de faire des recherches et d'essayer de résumer vos réponses. Les questions suivantes vous aideront à déterminer le type de destructeur dont vous avez besoin:
J'espère que ça aide.
* Il est important de noter qu'il n'y a aucun moyen en C++ de marquer une classe comme finale (c'est-à-dire non sous-classable). Par conséquent, dans le cas où vous décidez de déclarer votre destructeur non public et virtuel, rappelez-vous d'avertir explicitement vos collègues programmeurs de ne pas en dériver. de votre classe.
Les références:
Oui c'est toujours important. Les classes dérivées peuvent allouer de la mémoire ou conserver une référence à d'autres ressources qui devront être nettoyées lors de la destruction de l'objet. Si vous ne donnez pas de destructeurs virtuels à vos interfaces/classes abstraites, chaque fois que vous supprimez une instance de classe dérivée via une classe de base, le destructeur de votre classe dérivée ne sera pas appelé.
Par conséquent, vous ouvrez le potentiel de fuites de mémoire
class IFoo
{
public:
virtual void DoFoo() = 0;
};
class Bar : public IFoo
{
char* dooby = NULL;
public:
virtual void DoFoo() { dooby = new char[10]; }
void ~Bar() { delete [] dooby; }
};
IFoo* baz = new Bar();
baz->DoFoo();
delete baz; // memory leak - dooby isn't deleted
Ce n'est pas toujours requis, mais je trouve que c'est une bonne pratique. Cela permet de supprimer un objet dérivé en toute sécurité via un pointeur de type base.
Donc par exemple:
Base *p = new Derived;
// use p as you see fit
delete p;
est mal formé si Base
n'a pas de destructeur virtuel, car il tentera de supprimer l'objet comme s'il s'agissait d'un Base *
.
Ce n'est pas seulement une bonne pratique. C'est la règle n ° 1 pour toute hiérarchie de classe.
Maintenant pour le pourquoi. Prenez la hiérarchie typique des animaux. Les destructeurs virtuels passent par la répartition virtuelle comme tout autre appel de méthode. Prenons l'exemple suivant.
Animal* pAnimal = GetAnimal();
delete pAnimal;
Supposons que Animal est une classe abstraite. La seule façon pour C++ de connaître le destructeur approprié à appeler est via la répartition de méthode virtuelle. Si le destructeur n'est pas virtuel, il appellera simplement le destructeur d'Animal et ne détruira aucun objet dans les classes dérivées.
La raison pour laquelle le destructeur est virtuel dans la classe de base est qu’elle supprime simplement le choix des classes dérivées. Leur destructeur devient virtuel par défaut.
La réponse est simple, vous devez en avoir une virtuelle, sinon la classe de base ne serait pas une classe polymorphe complète.
Base *ptr = new Derived();
delete ptr; // Here the call order of destructors: first Derived then Base.
Vous préféreriez la suppression ci-dessus, mais si le destructeur de la classe de base n'est pas virtuel, seul le destructeur de la classe de base sera appelé et toutes les données de la classe dérivée resteront non supprimées.