Tout le monde sait que le destructeur de la classe de base doit généralement être virtuel. Mais qu'en est-il du destructeur de la classe dérivée ? En C++11, nous avons le mot-clé "override" et la possibilité d'utiliser explicitement le destructeur par défaut.
struct Parent
{
std::string a;
virtual ~Parent()
{
}
};
struct Child: public Parent
{
std::string b;
~Child() override = default;
};
Est-il correct d'utiliser les deux mots-clés "override" et "=default" dans le destructeur de la classe Child ? Le compilateur générera-t-il un destructeur virtuel correct dans ce cas ?
Si oui, alors pouvons-nous penser que c'est un bon style de codage, et que nous devrions toujours déclarer les destructeurs des classes dérivées de cette façon pour s'assurer que les destructeurs des classes de base sont virtuels ?
8 votes
Autant le faire
static_assert(std::has_virtual_destructor<Parent>::value, "contract violated");
3 votes
Notez qu'il n'est pas toujours nécessaire que le destructeur de la classe de base soit virtuel. Ce n'est donc (peut-être) une bonne idée que si c'est une exigence.
3 votes
Si cela fonctionne, j'aime bien, mais celui de Milleniumbug est meilleur (intention beaucoup plus claire). D'autre part, Stroustrup déteste les constructions "standard de codage" qui protègent contre les erreurs courantes, et insiste sur le fait que le compilateur devrait générer des avertissements appropriés, à la place.
3 votes
Je pense que l'approche de @milleniumbug exprime clairement l'intention. Si je tombais sur
~Child() override = default;
dans une base de code, je pourrais simplement supprimer la ligne.0 votes
Eh bien, l'utilisation de static_assert nécessite la même discipline de la part des programmeurs que l'utilisation de virtual destructor, donc je ne suis pas sûr que l'un soit meilleur que l'autre.
3 votes
@milleniumbug Je suis confus. Le site point de
override
-le uniquement est de forcer une erreur du compilateur si la méthode du parent n'est pasvirtual
. Comment unstatic_assert
une amélioration ?0 votes
@juanchopanza Cela dépend de l'utilisation que vous en faites. C'est une obligation dans certains cas (lorsque vous travaillez avec des pointeurs vers la classe de base).
1 votes
@Sandro Seulement quand vous supprimez lesdits pointeurs. Quoi qu'il en soit, je pense avoir mal interprété la fin de votre question.
2 votes
Le static_assert pourrait être placé dans un fichier cpp, ce qui rendrait plus pratique son ajout sans provoquer de recompilation - et le cacherait également comme un détail d'implémentation. Cependant, cela le rendrait plus difficile à trouver - et l'inconvénient de static_assert est que vous devez répéter le nom de la classe de base, alors que override ne contient que l'information minimale nécessaire.
1 votes
@juanchopanza Préférant les
static_assert
ressemble à de la folie. Si je tombais sur lestatic_assert
je le remplacerais paroverride
; c'est le point deoverride
alors questatic_assert
n'est qu'une façon confuse et non-idiomatique d'essayer d'écrire des protections verbeuses pour éviter de se tirer dans le pied. Chaque ligne de code est une responsabilité, surtout les lignes non idiomatiques et confuses.0 votes
@KyleStrand IYAM si vous devez faire face à quelqu'un qui vous enlève au hasard.
virtual
ness du destructeur dans votre classe de base, vous avez de plus gros problèmes à gérer (voir le message "contrat violé"). Et si une bibliothèque tierce fait cela, oh là là.1 votes
@milleniumbug C'est vrai, les deux constructions ne sont que des moyens de provoquer des erreurs de compilation si quelque chose se produit qui vraiment vraiment vraiment ne devrait pas se produire. Mais cela n'explique pas vraiment pourquoi la méthode moins idiomatique, qui utilise une réflexion sur les traits de type plus compliquée, est préférable.
0 votes
@KyleStrand le
static_assert
vous dit exactement ce qui est requis et est complètement idiomatique. Si static_assert est source de confusion, il peut être utile de prendre le temps d'étudier un peu de C++. L'autre option ressemble trop à du code redondant.6 votes
"il peut être utile de prendre un peu de temps pour étudier certains C++" -- veuillez voir "blâmer le programmeur" à la fin de ce poste . De plus, notez que je n'ai pas vraiment dit que je ne comprends pas le
static_assert
juste que c'est plus déroutant que leoverride
version. Ce qui est vrai, car elle est plus longue, plus verbeuse, et utilise une fonctionnalité relativement obscure de la bibliothèque standard.1 votes
KyleStrand Le point est que la vérification de la virtualité du destructeur de base n'est pas la responsabilité de la classe dérivée. C'est la responsabilité de ceux qui ont des pointeurs de base vers les objets de la classe dérivée et qui appellent
delete
sur eux.0 votes
@KyleStrand Pour préciser : si vous avez 30 classes dérivées, allez-vous toutes les annoter ? Non ? Bien. (remarque annexe :
std::unique_ptr
est un excellent endroit pour vérifier cela, mais je ne pense pas qu'une telle vérification soit requise par la norme)1 votes
@milleniumbug Je ne suis pas d'accord du point de vue de la conception. Les classes devraient être conçu afin qu'ils puissent être
delete
d en toute sécurité à partir d'un pointeur situé n'importe où dans la hiérarchie des classes ; la vérification que la classe a été correctement conçue de cette manière devrait pas être la préoccupation du programmeur, car nous devons nous attendre à ce que les classes soient bien conçues. (C'est la raison d'être des classes : subdiviser l'espace des problèmes en discret problèmes de conception).0 votes
Je pense cependant que le compilateur devrait émettre un avertissement lorsqu'un pointeur de classe de base est créé à partir d'une classe dérivée, mais que le destructeur de la classe de base n'est pas
virtual
car c'est le premier pas vers l'erreur.delete
situation. Malheureusement, je ne connais aucun compilateur qui implémente réellement cette option d'avertissement.2 votes
@milleniumbug Et, encore une fois, votre objection concernant l'annotation de 30 classes dérivées s'applique à les deux les constructions (
override
ystatic_assert
) tout aussi bien, et n'indique pas du tout qu'il faille être préféré plutôt que l'autre (bien que ce soit certainement un argument contre l'utilisation d'une telle annotation comme règle de base, comme le demande la question).3 votes
"Tout le monde sait que le destructeur de la classe de base doit généralement être virtuel." Ehm non, pas vraiment
2 votes
@LightnessRacesinOrbit Vous objectez à "tout le monde sait" ou à "il faut généralement être virtuel" ? (Ou les deux ?).
2 votes
@KyleStrand : Le dernier et, à peu près en conséquence, le premier.
0 votes
@KyleStrand Je ne vois pas en quoi un static_assert qui demande explicitement à la classe de base d'avoir un destructeur virtuel est plus déroutant que le fait de déclarer un destructeur dans votre classe lorsque vous n'en ont même pas besoin pour que vous puissiez vérifier la même chose. Et il faudra environ trois secondes pour expliquer le static_assert à tout programmeur compétent qui trouve cette festure obscure de quelque manière que ce soit.