J'ai essayé d'appeler ::delete
pour une classe dans le operator delete
de celui-ci. Mais le destructeur n'est pas appelé.
J'ai défini une classe MyClass
qui operator delete
a été surchargée. Le global operator delete
est également surchargé. La surcharge operator delete
de MyClass
nommerez mondiale surchargée operator delete
.
class MyClass
{
public:
MyClass() { printf("Constructing MyClass...\n"); }
virtual ~MyClass() { printf("Destroying MyClass...\n"); }
void* operator new(size_t size)
{
printf("Newing MyClass...\n");
void* p = ::new MyClass();
printf("End of newing MyClass...\n");
return p;
}
void operator delete(void* p)
{
printf("Deleting MyClass...\n");
::delete p; // Why is the destructor not called here?
printf("End of deleting MyClass...\n");
}
};
void* operator new(size_t size)
{
printf("Global newing...\n");
return malloc(size);
}
void operator delete(void* p)
{
printf("Global deleting...\n");
free(p);
}
int main(int argc, char** argv)
{
MyClass* myClass = new MyClass();
delete myClass;
return EXIT_SUCCESS;
}
La sortie est:
Newing MyClass...
Global newing...
Constructing MyClass...
End of newing MyClass...
Constructing MyClass...
Destroying MyClass...
Deleting MyClass...
Global deleting...
End of deleting MyClass...
Réel:
Il n'y a qu'un seul appel au destructeur avant d'appeler le surchargé operator delete
de MyClass
.
Attendu:
Il y a deux appels au destructeur. Un avant d'appeler le surchargé operator delete
de MyClass
. Un autre avant d'appeler le mondial operator delete
.
c++
delete-operator
expinc
la source
la source
MyClass::operator new()
devrait allouer de la mémoire brute, d'au moins (size
octets ) . Il ne doit pas tenter de construire complètement une instance deMyClass
. Le constructeur deMyClass
est exécuté aprèsMyClass::operator new()
. Ensuite, l'delete
expression dansmain()
appelle le destructeur et libère la mémoire (sans appeler à nouveau le destructeur). L'::delete p
expression ne contient aucune information sur le type dep
point d' objet , carp
est unvoid *
, elle ne peut donc pas appeler le destructeur.::delete p;
provoque un comportement indéfini car le type de*p
n'est pas le même que le type de l'objet à supprimer (ni une classe de base avec destructeur virtuel)void*
opérande était même explicitement mal formé. [expr.delete] / 1 : " L'opérande doit être du pointeur vers le type d'objet ou du type de classe. [...] Cela implique qu'un objet ne peut pas être supprimé à l'aide d'un pointeur de type void car void n'est pas un type d'objet. * "@OP J'ai modifié ma réponse.Réponses:
Vous abusez
operator new
etoperator delete
. Ces opérateurs sont des fonctions d'allocation et de désallocation. Ils ne sont pas responsables de la construction ou de la destruction d'objets. Ils sont uniquement responsables de fournir la mémoire dans laquelle l'objet sera placé.Les versions globales de ces fonctions sont
::operator new
et::operator delete
.::new
et::delete
sont nouvelles / delete-expressions, tout commenew
/delete
, différentes de celles dans la mesure où::new
et::delete
classe spécifiques contournentoperator new
/operator delete
surcharges.Les nouvelles / delete-expressions construisent / détruisent et allouent / désallouent (en appelant la appropriée
operator new
ouoperator delete
avant la construction ou après la destruction).Étant donné que votre surcharge n'est responsable que de la partie allocation / désallocation, elle doit appeler
::operator new
et::operator delete
au lieu de::new
et::delete
.L'
delete
entréedelete myClass;
est chargée d'appeler le destructeur.::delete p;
n'appelle pas le destructeur carp
a typevoid*
et par conséquent l'expression ne peut pas savoir quel destructeur appeler. Il appellera probablement votre remplaçant::operator delete
pour désallouer la mémoire, bien que l'utilisation d'unvoid*
opérande as pour une expression de suppression soit mal formée (voir modification ci-dessous).::new MyClass();
appelle votre remplacée::operator new
pour allouer de la mémoire et y construit un objet. Le pointeur vers cet objet est renvoyé quantvoid*
à la nouvelle expression dansMyClass* myClass = new MyClass();
, qui va ensuite construire un autre objet dans cette mémoire, mettant fin à la durée de vie de l'objet précédent sans appeler son destructeur.Éditer:
Grâce au commentaire de @ MM sur la question, j'ai réalisé qu'un
void*
opérande as::delete
est en fait mal formé. ( [expr.delete] / 1 ) Cependant, les principaux compilateurs semblent avoir décidé de ne prévenir que de cela, pas d'erreur. Avant de devenir mal formé, d'utiliser::delete
unvoid*
comportement déjà indéfini, voir cette question .Par conséquent, votre programme est mal formé et vous n'avez aucune garantie que le code fait réellement ce que j'ai décrit ci-dessus s'il parvient toujours à compiler.
Comme l'a souligné @SanderDeDycker ci-dessous sa réponse, vous avez également un comportement indéfini car en construisant un autre objet dans la mémoire qui contient déjà un
MyClass
objet sans appeler d'abord le destructeur de cet objet, vous violez [basic.life] / 5 qui interdit de le faire si le programme dépend des effets secondaires du destructeur. Dans ce cas, l'printf
instruction dans le destructeur a un tel effet secondaire.la source
Vos surcharges spécifiques à la classe ne sont pas effectuées correctement. Cela peut être vu dans votre sortie: le constructeur est appelé deux fois!
Dans la classe spécifique
operator new
, appelez directement l'opérateur global:De même, dans la classe spécifique
operator delete
, faites:Référez-vous à la
operator new
page de référence pour plus de détails.la source
Voir la référence du RPC :
Supprimer (et nouveau) ne sont responsables que de la partie «gestion de la mémoire».
Il est donc clair et attendu que le destructeur n'est appelé qu'une seule fois - pour nettoyer l'instance de l'objet. S'il était appelé deux fois, chaque destructeur devrait vérifier s'il avait déjà été appelé.
la source
operator delete()
fonction n'est pas la même chose qu'une expression de suppression. Le destructeur est appelé avant l'appel de laoperator delete()
fonction.