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
dont operator delete
A été surchargé. Le operator delete
Global est également surchargé. Le operator delete
Surchargé de MyClass
appellera le global operator delete
Surchargé.
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 operator delete
Surchargé de MyClass
.
Attendu:
Il y a deux appels au destructeur. Un avant d'appeler le operator delete
Surchargé de MyClass
. Un autre avant d'appeler le global operator delete
.
Vous utilisez mal operator new
Et operator 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 de nouvelles expressions/delete, tout comme new
/delete
, différentes de celles-ci, en ce que ::new
Et ::delete
Contournera les surcharges spécifiques à la classe operator new
/operator delete
.
Les nouvelles/delete-expressions construisent/détruisent et allouent/désallouent (en appelant le operator new
Ou operator delete
Avant construction ou après 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
.
Le delete
dans delete myClass;
Est responsable de l'appel du destructeur.
::delete p;
N'appelle pas le destructeur car p
a le type void*
Et par conséquent l'expression ne peut pas savoir quel destructeur appeler. Il appellera probablement votre ::operator delete
Remplacé pour désallouer la mémoire, bien qu'utilisant un void*
Comme opérande à une expression de suppression est mal formé (voir la modification ci-dessous).
::new MyClass();
appelle votre ::operator new
remplacé pour allouer de la mémoire et y construit un objet. Le pointeur vers cet objet est renvoyé sous la forme void*
À la nouvelle expression dans MyClass* myClass = new MyClass();
, qui construira ensuite 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 @ M.M sur la question, j'ai réalisé qu'un void*
Comme opérande de ::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 qu'il ne soit mal formé, utiliser ::delete
Sur un void*
Avait déjà un comportement non dé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 objet MyClass
sans appeler d'abord le destructeur de cet objet, vous violez [de base. life]/5 ce qui interdit de le faire si le programme dépend des effets secondaires du destructeur. Dans ce cas, l'instruction printf
dans le destructeur a un tel effet secondaire.
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:
return ::operator new(size);
De même, dans la classe spécifique operator delete
, faire:
::operator delete(p);
Se référer au operator new
page de référence pour plus de détails.
Voir Référence CPP :
operator delete
,operator delete[]
Désalloue le stockage précédemment alloué par un
operator new
. Ces fonctions de désallocation sont appelées par des expressions de suppression et par de nouvelles expressions pour désallouer la mémoire après avoir détruit (ou échoué à construire) des objets avec une durée de stockage dynamique. Ils peuvent également être appelés à l'aide de la syntaxe d'appel de fonction régulière.
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é.