31 votes

Destructeur virtuel avec des membres virtuels en C ++11

Dans ces diapositives sur le C++11/14 standard, sur la diapositive 15, l'auteur écrit que "de nombreux classiques de règles de codage [sont] n'est plus applicable" en C++11. Il propose une liste de trois exemples, et je suis d'accord avec la Règle de Trois et la gestion de la mémoire.

Cependant, son deuxième exemple est le "destructeur Virtuel avec virtual membres" (juste ça). Ça veut dire quoi? Je sais que l'on doit déclarer que le virtuel le destructeur de la classe de base pour appeler le droit destructeur si nous avons quelque chose comme

Base *b = new Derived;
...
delete b;

Ceci est bien expliqué ici: Quand utiliser les destructeurs virtuels?

Mais il est inutile maintenant en C++11 pour déclarer votre destructeur virtuel si vous avez des membres virtuels?

37voto

PeterSom Points 857

Comme l'auteur de la glisse, je vais essayer de clarifier.

Si vous écrivez du code explicitement l'allocation d'une Derived exemple avec new et les détruire avec delete à l'aide d'une classe de base pointeur ensuite, vous devez définir un virtual destructeur, sinon vous vous retrouvez avec incomplètement détruire l' Derived de l'instance. Cependant, je recommande de s'abstenir de l' new et delete complètement et utilisez exclusivement shared_ptr pour la référence à allouées sur la pile polymorphe des objets, comme des

shared_ptr<Base> pb=make_shared<Derived>();

De cette façon, le pointeur partagé garde la trace de l'original destructeur pour être utilisé, même si shared_ptr<Base> est utilisé pour la représenter. Une fois, la dernière en se référant shared_ptr est hors de portée ou est remis à zéro, ~Derived() sera appelée et de la mémoire libérée. Par conséquent, vous n'avez pas besoin de faire d' ~Base() virtuel.

unique_ptr<Base> et make_unique<Derived> ne proposent pas cette fonctionnalité, car ils ne donnent pas la mécanique de l' shared_ptr à l'égard de la deleter, car pointeur unique est beaucoup plus simple et vise le plus de frais généraux et n'est donc pas le stockage de l'extra pointeur de fonction nécessaires pour la deleter. Avec unique_ptr le deleter fonction fait partie du type et donc un uniqe_ptr avec un deleter se référant à l' ~Derived ne serait pas compatible avec un unique_ptr<Base> utilisent par défaut la deleter, qui serait mauvais pour un dérivé de l'instance de toute façon, si ~Base n'était pas virtuel.

Les suggestions que je fais, sont destinés à être facile à suivre et suivi tous ensemble. Ils essaient de produire du code plus simple, en permettant à tous de la gestion des ressources être fait par les composants de la bibliothèque et le compilateur de code généré.

La définition d'un (virtuel) destructeur dans une classe, aura pour effet d'interdire à un compilateur fourni déplacer constructeur/opérateur d'affectation et peut interdire également un compilateur fourni constructeur de copie/opérateur d'affectation dans les futures versions de C++. La résurrection est devenue facile avec =default, mais ressemble encore beaucoup de code réutilisable. Et le meilleur code est le code que vous n'avez pas à écrire, car il ne peut pas être mauvais (je sais qu'il y a toujours des exceptions à cette règle).

Pour résumer "Ne définissent pas un (virtuel) destructeur" qui est le corollaire de mon "la Règle du Zéro":

Lorsque vous créez un polymorphe (OO) la hiérarchie de classes en C++ moderne et voulez/besoin d'allouer ses instances sur le tas et y accéder via une classe de base pointeur utiliser make_shared<Derived>() de les instancier et d' shared_ptr<Base> pour les garder autour. Cela vous permet de garder la "Règle du Zéro".

Cela ne signifie pas que vous devez allouer tous les polymorphes objets sur le tas. Par exemple, la définition d'une fonction prenant un (Base&) comme paramètre, peut être appelée avec un local Derived variable sans problèmes et se comportera polymorphes, virtuels fonctions de membre de l' Base.

À mon avis, dynamique OO polymorphisme est fortement utilisées dans de nombreux systèmes. Nous ne devrions pas comme les programmes Java, lorsque nous utilisons C++, sauf si nous avons un problème, où polymorphisme dynamique avec des tas objets alloués est la bonne solution.

2voto

Tristan Brindle Points 5234

Je pense que c'est à voir avec la "règle de zéro", a mentionné ailleurs dans la présentation.

Si vous n'avez que des membres automatique des variables (c'est à dire utiliser shared_ptr ou unique_ptr pour les membres qui seraient autrement raw pointeurs), alors vous n'avez pas besoin d'écrire votre propre copie ou de déplacement des constructeurs ou des opérateurs d'affectation -- le compilateur fourni par défaut sera optimal. Dans la classe d'initialisation, vous n'avez pas besoin d'un constructeur par défaut soit. Et enfin, vous n'avez pas besoin d'écrire un destructeur à tous, virtuel ou non.

0voto

MSalters Points 74024

Le papier lié montre le code correspondant:

 std::unique_ptr<Derived> { new Derived };
 

Le deleter stocké est std::default_delete<Derived> , ce qui ne nécessite pas que Base::~Base soit virtuel.

Vous pouvez maintenant le déplacer vers un unique_ptr<Base> , et il déplacera également le std::default_delete<Derived> sans le convertir en std::default_delete<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