web-dev-qa-db-fra.com

GNU avertissement du compilateur "la classe a des fonctions virtuelles mais un destructeur non virtuel"

J'ai défini une interface en C++, c'est-à-dire une classe contenant uniquement des fonctions virtuelles pures.

Je veux interdire explicitement aux utilisateurs de l'interface de supprimer l'objet via un pointeur vers l'interface, j'ai donc déclaré un destructeur protégé et non virtuel pour l'interface, quelque chose comme:

class ITest{
public:
    virtual void doSomething() = 0;

protected:
    ~ITest(){}
};

void someFunction(ITest * test){
    test->doSomething(); // ok
    // deleting object is not allowed
    // delete test; 
}

Le compilateur GNU me donne un avertissement disant:

la classe 'ITest' a des fonctions virtuelles mais un destructeur non virtuel

Une fois le destructeur protégé, quelle différence y a-t-il à le rendre virtuel ou non virtuel?

Pensez-vous que cet avertissement peut être ignoré en toute sécurité ou réduit au silence?

58
Paolo Tedesco

C'est plus ou moins un bug dans le compilateur. Notez que dans les versions plus récentes du compilateur, cet avertissement n'est pas renvoyé (au moins dans 4.3, il ne le fait pas). Avoir le destructeur protégé et non virtuel est tout à fait légitime dans votre cas.

Voir ici pour un excellent article de Herb Sutter sur le sujet. De l'article:

Directive n ° 4: Un destructeur de classe de base doit être soit public et virtuel, soit protégé et non virtuel.

65
Greg Rogers

Certains des commentaires sur cette réponse se rapportent à une réponse antérieure que j'ai donnée, ce qui était faux.

Un destructeur protégé signifie qu'il ne peut être appelé qu'à partir d'une classe de base, pas par suppression. Cela signifie qu'un ITest * ne peut pas être supprimé directement, seule une classe dérivée le peut. La classe dérivée peut très bien vouloir un destructeur virtuel. Il n'y a rien de mal à votre code.

Cependant, comme vous ne pouvez pas désactiver localement un avertissement dans GCC, et que vous avez déjà une table virtuelle, vous pouvez envisager de rendre le destructeur virtuel de toute façon. Cela vous coûtera 4 octets pour le programme (pas par instance de classe), au maximum. Comme vous avez peut-être donné à votre classe dérivée un dtor virtuel, vous pouvez constater que cela ne vous coûte rien.

9
Airsource Ltd

Si vous insistez pour le faire, allez-y et passez -Wno-non-virtual-dtor à GCC. Cet avertissement ne semble pas être activé par défaut, vous devez donc l'avoir activé avec -Wall ou -Weffc++. Cependant, je pense que c'est un avertissement utile, car dans la plupart des situations, ce serait un bug.

4
bk1e

C'est une classe d'interface, il est donc raisonnable de ne pas supprimer les objets implémentant cette interface via cette interface. Un cas courant de cela est une interface pour les objets créés par une usine qui devrait être retournée à l'usine. (Avoir des objets contenant un pointeur vers leur usine peut être assez cher).

Je suis d'accord avec l'observation selon laquelle GCC se plaint. Au lieu de cela, il devrait simplement avertir lorsque vous supprimez un ITest *. C'est là que réside le vrai danger.

2
MSalters

Mon opinion personnelle est que vous feriez la bonne chose et que le compilateur est cassé. Je désactiverais l'avertissement (localement dans le fichier qui définit l'interface) si possible,

Je trouve que j'utilise beaucoup ce motif (petit "p"). En fait, je trouve qu'il est plus courant pour mes interfaces d'avoir des dtors protégés que pour eux d'avoir des dtors publics. Cependant, je ne pense pas que ce soit un idiome aussi courant (on n'en parle pas beaucoup) et je suppose que lorsque l'avertissement a été ajouté à GCC, il était approprié d'essayer de faire appliquer l'ancien dtor doit être virtuel si vous avoir la règle des fonctions virtuelles. Personnellement, j'ai mis à jour cette règle en `` dtor doit être virtuel si vous avez des fonctions virtuelles et souhaitez que les utilisateurs puissent supprimer des instances de l'interface via l'interface sinon le dtor devrait être protégé et non virtuel '' il y a longtemps;)

2
Len Holgate

Si le destructeur est virtuel, il s'assure que le destructeur de classe de base est également appelé pour effectuer le nettoyage, sinon des fuites peuvent résulter de ce code. Vous devez donc vous assurer que le programme n'a pas de tels avertissements (de préférence aucun avertissement du tout).

0
INS

Si vous aviez du code dans l'une des méthodes de ITest qui tentait de delete lui-même (une mauvaise idée, mais légale), le destructeur de la classe dérivée ne serait pas appelé. Vous devez toujours rendre votre destructeur virtuel, même si vous n'avez jamais l'intention de supprimer une instance dérivée via un pointeur de classe de base.

0
Adam Rosenfield