80 votes

Les destructeurs virtuels sont-ils hérités ?

Si j'ai une classe de base avec un destructeur virtuel. Une classe dérivée doit-elle aussi déclarer un destructeur virtuel ?

class base {
public:
    virtual ~base () {}
};

class derived : base {
public:
    virtual ~derived () {} // 1)
    ~derived () {}  // 2)
};

Des questions concrètes :

  1. Est-ce que 1) et 2) sont identiques ? Est-ce que 2) est automatiquement virtuel à cause de sa base ou est-ce qu'il "arrête" la virtualité ?
  2. Le destructeur dérivé peut-il être omis s'il n'a rien à faire ?
  3. Quelle est la meilleure pratique pour déclarer le destructeur dérivé ? Le déclarer virtuel, non-virtuel ou l'omettre si possible ?

95voto

Omnifarious Points 25666
  1. Oui, c'est la même chose. Le fait que la classe dérivée ne déclare pas quelque chose de virtuel ne l'empêche pas de l'être. En fait, il n'y a aucun moyen d'empêcher une méthode (y compris le destructeur) d'être virtuelle dans une classe dérivée si elle l'était dans une classe de base. En >=C++11, vous pouvez utiliser final pour l'empêcher d'être surchargée dans les classes dérivées, mais cela ne l'empêche pas d'être virtuelle.
  2. Oui, un destructeur dans une classe dérivée peut être omis s'il n'a rien à faire. Et le fait qu'il soit virtuel ou non n'a pas d'importance.
  3. Je l'omettrais si possible. Et j'utilise toujours soit le virtual mot-clé ou override pour les fonctions virtuelles dans les classes dérivées pour des raisons de clarté. Les gens ne devraient pas avoir à remonter toute la hiérarchie de l'héritage pour comprendre qu'une fonction est virtuelle. De plus, si votre classe est copiable ou déplaçable sans avoir à déclarer vos propres constructeurs de copie ou de déplacement, déclarer un destructeur de n'importe quelle sorte (même si vous le définissez comme default ) vous obligera à déclarer les constructeurs et opérateurs d'affectation copy et move si vous le souhaitez, car le compilateur ne les mettra plus à votre place.

Un petit point pour le point 3. Il a été souligné dans les commentaires que si un destructeur n'est pas déclaré, le compilateur en génère un par défaut (qui est toujours virtuel). Et ce destructeur par défaut est une fonction inline.

Les fonctions en ligne exposent potentiellement une plus grande partie de votre programme à des changements dans d'autres parties de votre programme et rendent la compatibilité binaire pour les bibliothèques partagées délicate. De plus, le couplage accru peut entraîner un grand nombre de recompilations face à certains types de changements. Par exemple, si vous décidez que vous voulez vraiment une implémentation pour votre destructeur virtuel, chaque morceau de code qui l'appelle devra être recompilé. Alors que si vous l'aviez déclaré dans le corps de la classe et que vous l'aviez ensuite défini vide dans une balise .cpp vous pouvez le modifier sans recompiler.

Mon choix personnel serait toujours de l'omettre lorsque cela est possible. À mon avis, cela encombre le code, et le compilateur peut parfois faire des choses légèrement plus efficaces avec une implémentation par défaut qu'avec une implémentation vide. Mais il y a des contraintes auxquelles vous pouvez être soumis qui font que c'est un mauvais choix.

0 votes

Votre dernière phrase devrait probablement se lire "ne devrait pas" au lieu de "devrait".

0 votes

@Chris Lutz, je suis en avance sur vous sur ce point. Il a été édité en soumission maintenant :-)

1 votes

Je ne suis pas d'accord avec la partie "omettre". Cela ne coûte pas grand chose de le déclarer dans l'en-tête et de le définir (corps vide) dans la source. Si vous le faites, vous pouvez toujours revenir et ajouter quelques étapes (journalisation ?) sans obliger vos clients à recompiler.

2voto

falstro Points 16545
  1. Le destructeur est automatiquement virtuel, comme toutes les méthodes. En C++, vous ne pouvez pas empêcher une méthode d'être virtuelle (si elle a déjà été déclarée virtuelle, c'est-à-dire qu'il n'y a pas d'équivalent de "final" en Java).
  2. Oui, il peut être omis.
  3. Je déclarerai un destructeur virtuel si j'ai l'intention que cette classe soit sous-classée, peu importe si elle sous-classe une autre classe ou non, je préfère également continuer à déclarer des méthodes virtuelles, même si ce n'est pas nécessaire. Cela permettra aux sous-classes de continuer à fonctionner, si jamais vous décidez de supprimer l'héritage. Mais je suppose que c'est juste une question de style.

0 votes

Les destructeurs ne sont pas automatiquement virtuels, tout comme les autres fonctions membres.

1 votes

@Neil ; bien sûr que non, je faisais référence à el destructeur dans l'exemple (c'est-à-dire lorsque la classe de base en possède un virtuel), et non les destructeurs en général. Et ceci est vrai pour toutes les méthodes, pas seulement pour les destructeurs.

1 votes

Depuis C++11, nous avons final .

1voto

Klaim Points 24511

Une fonction membre virtuelle rendra implicitement virtuelle toute surcharge de cette fonction.

Donc le virtuel dans 1) est "optionnel", le destructeur de la classe de base étant virtuel rend tous les destructeurs enfants virtuels aussi.

0voto

AProgrammer Points 31212

1/ Oui 2/ Oui, il sera généré par le compilateur. 3/ Le choix de le déclarer virtuel ou non devrait suivre votre convention pour les membres virtuels surchargés -- IMHO, il y a de bons arguments dans les deux sens, choisissez-en un et suivez-le.

Je l'omettrais si possible, mais il y a une chose qui peut vous inciter à le déclarer : si vous utilisez celui généré par le compilateur, il est implicitement inline. Il y a des moments où l'on veut éviter les membres inline (les bibliothèques dynamiques par exemple).

0voto

Alex Maystrenko Points 505

Les fonctions virtuelles sont surchargées implicitement. Lorsque la méthode d'une classe enfant correspond à la signature de la méthode de la fonction virtuelle d'une classe de base, elle est surchargée. Il est facile de créer des confusions et éventuellement de les rompre lors du remaniement, c'est pourquoi il existe des règles de surcharge pour les fonctions virtuelles. override y final depuis C++11 pour marquer ce comportement de manière explicite. Il existe des avertissements correspondants qui interdisent le comportement silencieux, par exemple -Wsuggest-override dans le GCC.

Il existe une question connexe pour override y final mots-clés sur SO : Le mot clé "override" permet-il simplement de vérifier si une méthode virtuelle a été surchargée ? .

Et la documentation dans la référence cpp https://en.cppreference.com/w/cpp/language/override

S'il faut utiliser override avec les destructeurs fait toujours l'objet d'un débat. Par exemple, voir la discussion dans cette question connexe de l'OS : surcharge par défaut du destructeur virtuel Le problème est que la sémantique du destructeur virtuel est différente de celle des fonctions normales. Les destructeurs sont enchaînés, de sorte que tous les destructeurs des classes de base sont appelés après le destructeur enfant. Cependant, dans le cas d'une méthode normale, les implémentations de base de la méthode surchargée ne sont pas appelées par défaut. Elles peuvent être appelées manuellement si nécessaire.

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