61 votes

Est-ce que chaque classe doit avoir un destructeur virtuel?

Java et C# supportent la notion de classes qui ne peuvent pas être utilisées comme classes de base avec les mots-clés final et sealed. En revanche, en C++, il n'y a pas de bonne façon d'empêcher une classe d'être dérivée, ce qui laisse à l'auteur de la classe un dilemme, est-ce que chaque classe devrait avoir un destructeur virtuel ou non ?


Édition : Depuis C++11, ce n'est plus vrai, vous pouvez spécifier qu'une classe est final.


D'une part, donner à un objet un destructeur virtuel signifie qu'il aura une vtable et donc qu'il consommera 4 (ou 8 sur les machines 64 bits) octets supplémentaires par objet pour le vptr.

D'autre part, si quelqu'un dérive plus tard de cette classe et supprime une classe dérivée via un pointeur vers la classe de base, le programme sera mal défini (en raison de l'absence d'un destructeur virtuel), et honnêtement, optimiser pour un pointeur par objet est ridicule.

D'un autre côté, avoir un destructeur virtuel (discutablement) annonce que ce type est destiné à être utilisé de manière polymorphe.

Certaines personnes pensent que vous avez besoin d'une raison explicite pour ne pas utiliser de destructeur virtuel (comme c'est le sous-texte de cette question) et d'autres disent que vous ne devriez les utiliser que lorsque vous avez des raisons de croire que votre classe est destinée à être dérivée, qu'en pensez-vous ?

1 votes

Il y a déjà des questions demandant les avantages et les inconvénients - est-ce un doublon, ou est-ce destiné à être un sondage d'opinion ? Si c'est le cas, peut-être devriez-vous créer des réponses "oui" et "non" pour le vote, puis fermer la question ? Je pense que c'est la façon recommandée de mettre en œuvre un sondage à choix multiples sur SO.

1 votes

7 votes

"et franchement, optimiser pour un pointeur par objet est ridicule." - Ce n'est pas ridicule pour les petits objets. C++0x ajoute un conteneur forward_list, précisément parce que parfois, un surcoût d'un pointeur par objet est trop important en termes d'espace et de temps.

65voto

Chaque classe abstraite doit soit avoir un,

  • destructeur protégé, ou,
  • destructeur virtuel.

Si vous avez un destructeur public non virtuel, ce n'est pas bon, car cela permet aux utilisateurs de supprimer à travers ce pointeur un objet dérivé. Comme nous le savons tous, c'est un comportement indéfini.

Pour une classe abstraite, vous avez déjà besoin d'un pointeur de table virtuelle dans l'objet, donc rendre le destructeur virtuel n'a pas (autant que je sache) un coût élevé en termes d'espace ou de performance d'exécution. Et cela a l'avantage que les classes dérivées ont automatiquement leur destructeur virtuel (voir le commentaire de @Aconcagua). Bien sûr, vous pouvez également rendre le destructeur protégé virtuel dans ce cas.

Pour une classe non abstraite non destinée à être supprimée à travers un pointeur vers elle, je ne pense pas qu'il y ait de bonnes raisons d'avoir un destructeur virtuel. Cela gaspillerait des ressources, mais plus important encore, cela donnerait aux utilisateurs un mauvais indice. Pensez simplement à quel sens étrange cela aurait de donner à std::iterator un destructeur virtuel.

3 votes

Ou struct tm de , qui cesserait d'être POD et ne serait donc plus compatible avec les conventions d'appel de C.

0 votes

Super réponse, litb. Je vais aller modifier tout mon code maintenant.

0 votes

Ajout tardif, je sais - mais je ne suis pas d'accord sur le destructeur protégé. Quelqu'un pourrait négliger la nécessité de rendre à nouveau virtuel le destructeur public de la classe dérivée, et quelqu'un d'autre pourrait dériver de la dérivée en supposant un destructeur déjà virtuel - et supprimer (illégalement) le petit-enfant via un pointeur vers l'enfant... Bien sûr, la deuxième personne a alors commis une erreur, mais elle aurait été évitée si le destructeur était déjà virtuel dans la toute première classe de base, donc d'un point de vue de sécurité, je ne considère que la deuxième option comme valide (classes abstraites uniquement, bien sûr).

33voto

jalf Points 142628

La question est vraiment de savoir si vous voulez forcer des règles sur la façon dont vos classes doivent être utilisées? Pourquoi? Si une classe n'a pas de destructeur virtuel, toute personne utilisant la classe sait qu'elle n'est pas censée être dérivée et quelles limites s'appliquent si vous essayez quand même. N'est-ce pas suffisant?

Ou avez-vous besoin que le compilateur lance une erreur grave si quelqu'un ose faire quelque chose que vous n'aviez pas anticipé?

Donnez à la classe un destructeur virtuel si vous avez l'intention que les gens en dérivent. Sinon, ne le faites pas et supposez que toute personne utilisant votre code est suffisamment intelligente pour utiliser correctement votre code.

14 votes

@if they derive from it AND want to use it in a polymorphic way/

12voto

Nemanja Trifunovic Points 17239

Non! Les destructeurs virtuels ne sont utilisés que lorsque un objet d'une classe dérivée est supprimé via un pointeur de classe de base. Si votre classe n'est pas destinée à servir de base dans ce scénario, ne déclarez pas le destructeur comme virtuel - vous enverriez un message incorrect.

0 votes

Alors, comment anticiper chaque cas où votre code est utile? Peut-être y aura-t-il un cas où votre classe, qui est censée être utilisée de manière non polymorphe par vous, n'est utile que comme classe de base dans un cas très spécifique.

10voto

Naveen Points 37095

Cela devrait répondre à votre question : Quand le destructeur doit-il être virtuel

2voto

Evan Teran Points 42370

Je répondrais "non" à la question générale. Ce n'est pas chaque classe qui en a besoin. Si vous savez que la classe ne devrait jamais être héritée, alors il n'est pas nécessaire de subir le surcoût mineur. Mais s'il y a une chance, il vaut mieux être prudent et en mettre un.

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