84 votes

Classe dérivée avec destructeur non virtuel

Existe-t-il des circonstances dans lesquelles il est légitime pour une classe dérivée d'avoir une classe non dérivée ? virtual destructeur ? Un non virtual destructeur signifie qu'une classe ne doit pas être utilisée comme classe de base. Le fait d'avoir une classe non virtual d'une classe dérivée agit comme une forme faible de la méthode Java final modificateur ?

Je suis particulièrement intéressé par le cas où la classe de base de la classe dérivée possède une classe de type virtual destructeur.

113voto

In silico Points 30778

Existe-t-il des circonstances dans lesquelles il est légitime qu'une entreprise dérivée ait un destructeur non virtuel ?

Oui.

Un destructeur non virtuel signifie qu'une classe ne doit pas être utilisée comme classe de base.

Pas vraiment ; un destructeur non virtuel signifie que la suppression d'une instance de derived via un base Le pointeur ne fonctionne pas. Par exemple :

class Base {};
class Derived : public Base {};

Base* b = new Derived;
delete b; // Does not call Derived's destructor!

Si vous ne faites pas delete de la manière décrite ci-dessus, alors tout ira bien. Mais si c'est le cas, alors vous utilisez probablement la composition et non l'héritage.

Est-ce que le fait d'avoir un destructeur non virtuel d'une classe dérivée agira comme un destructeur de classe ? forme faible du modificateur final de Java ?

Non, parce que virtual -s'étend aux classes dérivées.

class Base
{
public:
    virtual ~Base() {}
    virtual void Foo() {};
};

class Derived : public Base
{
public:
    ~Derived() {}  // Will also be virtual
    void Foo() {}; // Will also be virtual
};

Il n'y a pas de mécanisme intégré dans le langage C++03 ou antérieur pour empêcher les sous-classes(*). Ce qui n'est pas vraiment un problème de toute façon puisque vous devriez toujours préfèrent la composition à l'héritage . En d'autres termes, utilisez l'héritage lorsqu'une relation "is-a" est plus logique qu'une véritable relation "has-a".

(*) Le modificateur "final" a été introduit en C++11.

39voto

Alok Save Points 115848

Il est parfaitement valable d'avoir une classe de base avec un destructeur non virtuel si vous n'allez jamais appeler delete sur un pointeur de la classe de base pointant vers un objet de la classe dérivée.

Suivez les conseils d'Herb Sutter :

Ligne directrice n° : Seulement si les classes dérivées ont besoin d'invoquer l'implémentation de base d'une fonction virtuelle, rendez la fonction virtuelle protégée. Pour le cas particulier du destructeur uniquement :

Ligne directrice n° : Le destructeur d'une classe de base doit être soit public et virtuel, soit protégé et non virtuel.


Peut-être que votre question est en fait :
Est-ce que le destructeur dans Dérivé doit être virtuelle si le destructeur de la classe de base est virtuel ?

La réponse est NON.
Si le destructeur de la classe de base est virtuel, alors le destructeur de la classe dérivée est déjà implicitement virtuel, vous n'avez pas besoin de le spécifier comme virtuel explicitement.

17voto

Aborder la dernière édition :

Edit : Je suis particulièrement intéressé par le cas où la classe de base de la classe dérivée a un destructeur virtuel.

Dans ce cas, le destructeur de la classe dérivée sera sera virtuelle, que vous ajoutiez ou non l'option virtual mot-clé ou non :

struct base {
   virtual ~base() {}       // destructor is virtual
};
struct derived : base {
   ~derived() {}            // destructor is also virtual, because it is virtual in base
};

Cela ne se limite pas aux destructeurs. Si, à un moment quelconque de la hiérarchie des types, un membre de fonction est déclaré virtuel, toutes les surcharges de cette même fonction seront virtuelles, qu'elles soient déclarées comme telles ou non. La particularité des destructeurs est que ~derived() Remplacer virtual ~base() même si le nom du membre diffère - c'est la seule spécificité des destructeurs ici.

5voto

James Kanze Points 96599

Votre question n'est pas vraiment claire. Si la classe de base a un destructeur virtuel virtuel, la classe dérivée en aura un, de toute façon. Il n'y a aucun moyen de désactiver la virtualité, une fois qu'elle a été déclarée.

Et il y a certainement des cas où il est logique de dériver d'une qui n'a pas de destructeur virtuel. La raison pour laquelle le destructeur de la classe de base de la classe de base devrait être virtuelle, c'est pour que vous puissiez effacer à travers un pointeur vers la classe de base. Si la dérivation est privée, vous ne devez pas vous vous n'avez pas à vous soucier de cela, puisque votre Derived* ne sera pas converti en un Base* . Sinon, j'ai vu la recommandation que si la classe de base de base n'est pas virtuel, il doit être protégé ; cela évite le seul cas de comportement cas de comportement indéfini (suppression par un pointeur vers la base) qui pourrait se produire pourrait se produire. Dans la pratique, cependant, beaucoup de classes de base (par ex. std::iterator<> ) ont une sémantique telle qu'il n'arrive même pas à de créer des pointeurs vers eux, et encore moins de les supprimer par le biais de ces pointeurs. pointeurs. Par conséquent, l'ajout d'une protection peut représenter plus d'efforts qu'il n'en vaut la peine.

4voto

Nemanja Trifunovic Points 17239

Cela dépend de l'objectif de votre classe. Parfois, c'est une bonne pratique de faire en sorte que votre destructeur soit protégé, mais pas virtuel - ce qui dit en gros : "Vous ne devez pas supprimer un objet de la classe dérivée via un pointeur de type de base".

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