2009-10-23 6 views
9

Comment est-il possible de supprimer un objet avec un destructeur privé dans le code suivant? J'ai réduit le programme réel à l'exemple suivant, mais il compile et fonctionne toujours.Suppression d'un objet avec un destructeur privé

class SomeClass; 

int main(int argc, char *argv[]) 
{ 
    SomeClass* boo = 0; // in real program it will be valid pointer 
    delete boo; // how it can work? 

    return -1; 
} 

class SomeClass 
{ 
private: 
    ~SomeClass() {}; // ! private destructor ! 
}; 
+0

Interesting..if Je propose la définition de la classe ci-dessus principale(), il renvoie une erreur du compilateur. Sinon, je viens d'obtenir un 'C4150 d'avertissement: suppression du pointeur vers le type incomplet 'SomeClass'; aucun destructeur appelé ' – Naveen

+1

@Naveen: C'est prévu. Le type incomplet est un problème. Le destructeur privé en est un autre. Chacun a son propre message de diagnostic. Vous basculez entre les deux en déplaçant la définition de 'SomeClass'. – AnT

Répondre

15

Vous essayez de supprimer l'objet de type de classe incomplète. C++ standard dit que vous aurez un comportement non défini dans ce cas (5.3.5/5):

Si l'objet supprimé est de type classe incomplète au moment de la suppression et la classe complète a un non-trivial destructeur ou une fonction de désallocation, le comportement est indéfini.

Pour détecter ces cas, vous pouvez utiliser boost::checked_delete:

template<typename T> 
inline void checked_delete(T* p) 
{ 
    typedef char type_must_be_complete[ sizeof(T)? 1: -1 ]; 
    (void) sizeof(type_must_be_complete); 
    delete p; 
} 
+1

Cela profite du fait que sizeof() ne retourne jamais 0 même pour des structures/classes vides, ce que j'avais oublié. ISO-IEC-14882 section 9.1: "Les objets complets et les sous-objets membres du type de classe doivent avoir une taille différente de zéro." L'implication est que chaque objet dans un tableau aura une adresse mémoire unique. Probablement la même chose pour les structures - est-ce aussi le cas en C? – leander

+0

En C de structure vide est égal à 0. –

+0

@leander: Pas exactement. Vous voyez en C++ qu'il est simplement * illégal * d'appliquer 'sizeof' à un type incomplet. C'est ce que le code ci-dessus essaie d'attraper par la partie '(void) sizeof ...'. Cette expression doit arrêter la compilation sur un compilateur conforme à la norme en émettant une * erreur *. Cependant, si un compilateur étrange arrive à autoriser 'sizeof' sur des types incomplets (comme une extension) avec 0 résultat, alors la partie' typedef ... 'les attrapera et forcera un échec de 'tableau de taille négative'. Donc, vous avez un "piège" principal ici (la partie '(vide)') et un piège auxiliaire "juste au cas" (la partie 'typedef'). – AnT

7

Ce code provoque un comportement indéfini (UB). Il est UB en C++ à delete un objet de type incomplet ayant un destructeur non trivial. Et dans votre code le type SomeClass est incomplet au point de delete, et il a un destructeur non trivial. Les compilateurs émettent généralement un avertissement à ce sujet, car en C++ formellement ce n'est pas une violation de contrainte. Donc, à proprement parler, votre code ne "fonctionne" pas. Il simplement compile et fait quelque chose undefined lors de l'exécution.

Le compilateur n'est tout simplement pas nécessaire pour intercepter cette erreur. La raison en est que cela pourrait être parfaitement bien si votre objet possède un destructeur trivial. Le compilateur n'a aucun moyen de savoir quel genre de destructeur ce type aura finalement, donc il ne peut pas dire avec certitude s'il s'agit d'une erreur ou non.

+0

Ce serait bien si (1) 'T' a un destructeur trivial (2)' T' ne redéfinit pas 'delete operator'' – curiousguy

4

Car le type SomeClass n'est pas complètement déclaré lors de l'appel operator delete.

La suppression d'un tel pointeur est un comportement indéfini, mais en pratique, la plupart des compilateurs libéreraient simplement la mémoire (si le pointeur n'était pas NULL) et n'appelleraient pas le destructeur.

Par exemple, g ++ vous donnera un avertissement au sujet de cette question:

foo.cpp: In function 'int main(int, char**)': 
foo.cpp:6: warning: possible problem detected in invocation of delete operator: 
foo.cpp:5: warning: 'boo' has incomplete type 
foo.cpp:1: warning: forward declaration of 'struct SomeClass' 
foo.cpp:6: note: neither the destructor nor the class-specific operator delete will be called, even if they are declared when the class is defined. 
+1

" Supprimer un tel pointeur ne ferait que libérer la mémoire ... "- NON! Le comportement est * UNDEFINED *, c'est-à-dire que toute supposition sur ce qui "passe" essentiellement est invalide. – DevSolar

+1

@DevSolar: Vrai, il est indéfini selon la norme. En pratique, c'est ce qui se passera sur la plupart des compilateurs. J'ai mis à jour la réponse pour refléter cela. – laalto

+0

J'ai un peu de mal à m'appuyer sur quoi que ce soit en dehors du domaine bien-conduit. Surtout dans le code des collègues. Ça me fait remonter le mur. Je blâme les réponses comme les vôtres. (;-) << - smiley important) – DevSolar

Questions connexes