utilité de redéfinir le destructeur virtuel quand il n'y a rien a nettoyer

Le
Marc G
Bonsoir,
Tout ou presque est dans l'objet de ma question.
J'ai une hiérarchie de classes assez simples qui dérivent toutes de la même
classe de base
et auxquelles j'accède classiquement à partir de pointeurs alloués dans le
tas sur la classe de base.
Les destructeurs associés à ces classes ne font rien
=> d'où ma question : est-il utile de les définir et est-il utile dans ce
cas de définir un destructeur virtuel dans la classe de base.
A priori, ça fait des fonctions virtuelles inutiles (elles ne font rien) ?
Marc
Questions / Réponses high-tech
Vidéos High-Tech et Jeu Vidéo
Téléchargements
Vos réponses Page 1 / 2
Gagnez chaque mois un abonnement Premium avec GNT : Inscrivez-vous !
Trier par : date / pertinence
Fabien LE LEZ
Le #19916211
On Tue, 11 Aug 2009 22:31:34 +0200, "Marc G"
et auxquelles j'accède classiquement à partir de pointeurs alloués dans le
tas sur la classe de base.



Ton code doit ressembler à ça :

class Base
{
public:
// ...
virtual ~Base() {}
};

class Derivee: public Base
{
// Inutile de répéter ici que ~Derivee()
// est virtuel, c'est implicite.
};

int main()
{
Base* ptr= new Derivee;
delete ptr;
}

Si les destructeurs sont virtuels, tout va bien : le compilateur sait
qu'il doit détruire un objet de classe "Derivee".

En revanche, si ~Base() n'est pas virtuel, le compilateur croit
détruire un "Base" (puisque tu passes un "Base*" à delete). Comme
l'objet est en réalité un "Derivee", on peut s'attendre à des
problèmes.

A priori, ça fait des fonctions virtuelles inutiles (elles ne font rien) ?



Note que toute classe a un constructeur de copie, un opérateur de
copie, et un destructeur, quoi qu'il arrive. Si tu ne les explicites
pas, ils sont implicites, mais ils existent.
Donc, expliciter une de ces fonctions ne rajoute aucune fonction
membre à la classe.

Par ailleurs, si une fonction est virtuelle dans la classe de base,
elle l'est aussi automatiquement dans la classe dérivée. Donc, dans
ton cas, non, il n'est pas utile de parler des destructeurs dans les
classes dérivées, uniquement dans la classe de base.
Fabien LE LEZ
Le #19916201
On Tue, 11 Aug 2009 22:31:34 +0200, "Marc G"
est-il utile dans ce
cas de définir un destructeur virtuel dans la classe de base.



Pour conclure et compléter mon message précédent : si une classe a au
moins une fonction virtuelle, son destructeur doit aussi être virtuel.
D'ailleurs, il me semble que g++ râle si ce n'est pas le cas.
Michael Doubez
Le #19916841
On 12 août, 00:01, Fabien LE LEZ
On Tue, 11 Aug 2009 22:31:34 +0200, "Marc G"
>est-il utile dans ce
>cas de définir un destructeur virtuel dans la classe de base.

Pour conclure et compléter mon message précédent : si une classe a au
moins une fonction virtuelle, son destructeur doit aussi être virtuel.
D'ailleurs, il me semble que g++ râle si ce n'est pas le cas.



D'ailleurs est ce qu'une classe qui a seulement son destructeur
virtuel est toujours une "standard layout class" (§9/7 de n2914) ? Pas
d'après la clause.

Ca pourrait être une incitation à ne pas mettre le destructeur virtuel
(pour pouvoir utiliser offsetof() par exemple).

--
Michael
Jean-Marc Bourguet
Le #19916971
"Marc G"
Bonsoir,
Tout ou presque est dans l'objet de ma question.
J'ai une hiérarchie de classes assez simples qui dérivent toutes de la même
classe de base
et auxquelles j'accède classiquement à partir de pointeurs alloués dans le
tas sur la classe de base.
Les destructeurs associés à ces classes ne font rien
=> d'où ma question : est-il utile de les définir et est-il utile dans ce
cas de définir un destructeur virtuel dans la classe de base.



-> Est-ce que les objects sont detruits a partir d'un pointeur vers la
classe de base ou vers la classe la plus derivee. Dans le premier cas, un
destructeur virtuel est necessaire (meme si en pratique on doit pouvoir
trouver des cas particuliers ca marche pour une version donnee du
compilateur et un ensemble donne de flags de compilation).

-> "Ne fais rien", tu oublies vraisemblablement qu'il y a les destructeurs
des membres ajoutes.

A+

--
Jean-Marc
FAQ de fclc++: http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ
C++ FAQ Lite en VF: http://www.ifrance.com/jlecomte/c++/c++-faq-lite/index.html
Site de usenet-fr: http://www.usenet-fr.news.eu.org
James Kanze
Le #19916961
On Aug 12, 12:01 am, Fabien LE LEZ
On Tue, 11 Aug 2009 22:31:34 +0200, "Marc G"


>est-il utile dans ce cas de définir un destructeur virtuel
>dans la classe de base.



Pour conclure et compléter mon message précédent : si une
classe a au moins une fonction virtuelle, son destructeur doit
aussi être virtuel.



Il n'y a rien dans la norme qui dit ça.

D'ailleurs, il me semble que g++ râle si ce n'est pas le cas.



Pas chez moi. (Tous les compilateurs ont des warnings stupids
qu'il faut supprimer.)

Selon la norme, le seul cas où un destructeur virtuel est
nécessaire, c'est si tu fais un delete, et le type dynamique
de l'objet n'est pas le même que le type statique du pointeur.
Dans la pratique, je crois que la règle, c'est plutôt de ne pas
dériver d'une classe au moins que :
-- elle a un destructeur virtuel, ou
-- son destructeur est protégé, ou
-- on ne risque pas de l'allouer dynamiquement, ou
-- tout simplement elle a une sémantique telle qu'on n'en
aurait jamais des pointeurs vers elle.
L'avant-dernière condition n'est pas toujours facile à évaluer :
clairement, si le type de base a déclaré les operator new et
operator delete privé, mais je pense aussi à des cas où on
dérive de std::vector pour une utilisation très spéciale. Quant
à la dernière condition, je pense à des cas comme std::iterator.
En général, on peut fournir de tels classes avec un destructeur
protégé, mais les instances dans la norme ne le font pas, et je
ne suis pas sûr que ce soit une bonne idée ; dès que le
programmeur déclare un destructeur, le destructeur n'est pas
trivial, même dans les classes dérivées.

--
James Kanze (GABI Software) email:
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
Marc G
Le #19917621
> -> "Ne fais rien", tu oublies vraisemblablement qu'il y a les destructeurs
des membres ajoutes.


oui, mais ils "s'auto-détruisent" :-)
Mes destructreurs sont tous du type
virtual ~ClasseDerivee() {}
ie qu'ils ne font pas grand chose.
Mais je crois comprendre que si je fais un delete à partir d'un pointeur de
la classe de base,
le destructeur de celle-ci doit être déclaré virtuel même dans ce cas...
Merci à vous tous.
Marc
Jean-Marc Bourguet
Le #19917611
"Marc G"
> -> "Ne fais rien", tu oublies vraisemblablement qu'il y a les destructeurs
> des membres ajoutes.
oui, mais ils "s'auto-détruisent" :-)



Il faut qu'ils soient appeles quand meme. Tu peux considerer en premiere
approximation -- ca ne tient pas compte des classes de base virtuelles --
qu'ils sont appeles par le destructeur de la classe apres execution du code
specique, tout comme ils sont construits par les constructeurs avant
execution du code specifique (pour les constructeurs, la liste
d'initialisation permet de passer des parametres; il n'y a pas d'equivalent
pour les destructeurs).

Mes destructreurs sont tous du type
virtual ~ClasseDerivee() {}
ie qu'ils ne font pas grand chose.
Mais je crois comprendre que si je fais un delete à partir d'un pointeur de
la classe de base,
le destructeur de celle-ci doit être déclaré virtuel même dans ce cas...


Tu as bien compris.

Tu n'as pas besoin d'ecrire un destructeur vide dans les classes
descendantes (il sera genere par le compilateur dans quasiment tous les
cas), mais le destructeur de la classe de base doit etre virtuel.

A+

--
Jean-Marc
FAQ de fclc++: http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ
C++ FAQ Lite en VF: http://www.ifrance.com/jlecomte/c++/c++-faq-lite/index.html
Site de usenet-fr: http://www.usenet-fr.news.eu.org
Fabien LE LEZ
Le #19920111
On Wed, 12 Aug 2009 00:44:48 -0700 (PDT), James Kanze

Pour conclure et compléter mon message précédent : si une
classe a au moins une fonction virtuelle, son destructeur doit
aussi être virtuel.



Il n'y a rien dans la norme qui dit ça.



Non. Disons que c'est une règle façon "recette de cuisine", facile à
suivre tant qu'on n'a pas compris tous les tenants et aboutissants.

En l'occurence, si ta classe a des fonctions virtuelles, c'est que tu
comptes utiliser le polymorphisme d'héritage, et avoir des pointeurs
(ou références) sur la classe de base. Dans ce cas, avoir un
destructeur non virtuel, c'est jouer avec le feu, et comme on a déjà
une vtable, déclarer le destructeur virtuel ne coûte pas plus cher.

D'ailleurs, il me semble que g++ râle si ce n'est pas le cas.


Pas chez moi. (Tous les compilateurs ont des warnings stupids
qu'il faut supprimer.)



En fait, je crois que g++ râle quand on dérive d'une classe qui n'a
pas de destructeur virtuel. Ce qui peut s'avérer un bon warning,
suivant la situation.
James Kanze
Le #19921111
On Aug 12, 9:42 pm, Fabien LE LEZ
On Wed, 12 Aug 2009 00:44:48 -0700 (PDT), James Kanze



>> Pour conclure et compléter mon message précédent : si une
>> classe a au moins une fonction virtuelle, son destructeur doit
>> aussi être virtuel.



>Il n'y a rien dans la norme qui dit ça.



Non. Disons que c'est une règle façon "recette de cuisine",
facile à suivre tant qu'on n'a pas compris tous les tenants et
aboutissants.



En l'occurence, si ta classe a des fonctions virtuelles, c'est
que tu comptes utiliser le polymorphisme d'héritage, et avoir
des pointeurs (ou références) sur la classe de base. Dans ce
cas, avoir un destructeur non virtuel, c'est jouer avec le
feu, et comme on a déjà une vtable, déclarer le destructeur
virtuel ne coûte pas plus cher.



Dans l'ensemble, je suis d'accord. Sauf que je le complèrerai :
soit un destructeur virtuel, soit un destructeur protégé. Mais
il y a bien des exceptions, qu'il faut aussi comprendre.

>> D'ailleurs, il me semble que g++ râle si ce n'est pas le
>> cas.
>Pas chez moi. (Tous les compilateurs ont des warnings stupids
>qu'il faut supprimer.)



En fait, je crois que g++ râle quand on dérive d'une classe
qui n'a pas de destructeur virtuel. Ce qui peut s'avérer un
bon warning, suivant la situation.



Toujours pas avec les options que j'utilise (-std=c++98
-pedantic -ffor-scope -fno-gnu-keywords -foperator-names -pipe
-Wall -W -Woverloaded-virtual -Wno-sign-compare -Wno-deprecated
-Wno-non-virtual-dtor -Wpointer-arith -Wno-unused -Wno-switch
-Wno-missing-braces -Wno-long-long -static-libgcc -ggdb3
-D_GLIBCXX_CONCEPT_CHECKS -D_GLIBCXX_DEBUG
-D_GLIBCXX_DEBUG_PEDANTIC). Non par défaut (sans options) non
plus. En tant qu'avertissement, je ne le trouve pas très utile ;
il y a trop d'autres choses à considérer quand on dérive, et la
plupart sont bien plus subtile. Du coup, j'ai l'impression que
si on est assez averti d'avoir considéré les autres aspects, et
qu'on dérive quand même d'une classe sans destructeur virtuel,
on le fait expres, et qu'on sait ce qu'on fait.

--
James Kanze (GABI Software) email:
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
James Kanze
Le #19921151
On Aug 12, 12:50 pm, "Marc G"
[...]
Mes destructreurs sont tous du type
virtual ~ClasseDerivee() {}



Ce qui ne veut pas dire qu'ils ne fassent rien:-). Un
destructeur n'est trivial que s'il est généré implicitement,
qu'il n'est pas virtuel, et que tous les destructeurs des
classes de base directes et des membres soient triviaux aussi.
Or, dans la mesure où il te faut bien un destructeur virtuel
dans la classe de base (ce qui fait que tous les classes
dérivées auront des destructeurs virtuels aussi, même si c'est
le compilateur qui les génère), aucun destructeur dans
l'hièrarchie n'est trivial.

ie qu'ils ne font pas grand chose.



Sauf appeler les destructeurs des membres et des classes de
base, et (éventuellement) modifier le vptr.

Mais je crois comprendre que si je fais un delete à partir
d'un pointeur de la classe de base, le destructeur de celle-ci
doit être déclaré virtuel même dans ce cas...



Tout à fait. Dans la pratique, c'est grace au destructeur de la
classe la plus dérivée que le compilateur trouve l'adresse à
passer à la fonction operator new.

--
James Kanze (GABI Software) email:
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
Publicité
Poster une réponse
Anonyme