Quelle est la bonne méthode pour supprimer toute la mémoire allouée ici?
const char* charString = "Hello, World";
void *mem = ::operator new(sizeof(Buffer) + strlen(charString) + 1);
Buffer* buf = new(mem) Buffer(strlen(charString));
delete (char*)buf;
OR
const char* charString = "Hello, World";
void *mem = ::operator new(sizeof(Buffer) + strlen(charString) + 1);
Buffer* buf = new(mem) Buffer(strlen(charString));
delete buf;
ou sont-ils tous les deux identiques?
La bonne méthode est:
buf->~Buffer();
::operator delete(mem);
Vous ne pouvez supprimer qu'avec delete
opérateur ce que vous avez reçu de new
opérateur. Si vous appelez directement le operator new
, vous devez également appeler directement la fonction operator delete
, et doit également appeler manuellement le destructeur.
Il existe deux notions distinctes en C++:
Le nouveau/supprimer opérateurs.
Nouveau/Supprimer expressions.
Les opérateurs allouent et désallouent de la mémoire. L'expression new
construit des objets. L'expression delete
détruit parfois un objet et appelle l'opérateur.
Pourquoi "parfois"? Parce que cela dépend de l'expression. Le nu, global new
appelle d'abord operator-new pour allouer de la mémoire puis construit l'objet; le global delete
appelle le destructeur et libère la mémoire. Mais toutes les autres surcharges de new
et delete
sont différentes:
Les opérateurs New/Delete doivent toujours être surchargés par paires correspondantes, car l'opérateur de suppression correspondant est appelé lorsqu'un constructeur d'objet lève une exception. Cependant, il n'existe aucun moyen automatique d'invoquer le destructeur pour un objet qui a été alloué avec un opérateur new
surchargé, vous devez donc le faire vous-même.
Comme premier et plus simple exemple, considérons l'opérateur placement-new , qui est mandaté pour prendre la forme void * operator new (size_t, void * p) throw() { return p; }
. L'opérateur delete
correspondant est donc mandaté pour ne rien faire: void operator delete (void *, void *) throw() { }
. Usage:
void * p = ::operator new(5); // allocate only!
T * q = new (p) T(); // construct
q->~T(); // deconstruct: YOUR responsibility
// delete (p) q; <-- does not exist!! It would invoke the following line:
::operator delete(p, q); // does nothing!
::operator delete(q); // deallocate
En supposant qu'il n'y a rien de tel que Buffer::operator delete
, les delete buf;
la version est correcte et fera tout le nettoyage approprié. Pour être un peu plus sûr, vous pouvez dire ::delete buf;
.
Le débat sur la langue et les avocats suit.
5.3.5/1
L'opérateur delete-expression détruit un objet dérivé (1.8) ou un tableau créé par un new-expression.
expression-suppression:
::
optdelete
expression-cast::
optdelete [ ]
expression-castLa première alternative est pour les objets non-tableau, et la seconde est pour les tableaux. ...
5.3.5/2
... Dans la première alternative (delete object), la valeur de l'opérande de
delete
peut être un pointeur nul, un pointeur vers un objet non tableau créé par un précédent - new-expression, ou un pointeur vers un sous-objet (1.8) représentant une classe de base d'un tel objet (article 10). Sinon, le comportement n'est pas défini.
Le pointeur doit donc pointer sur un objet créé par un new-expression, qui est défini:
5.3.4/1
nouvelle-expression:
::
optnew
nouveau-placementopt new-type-id _new-initializer_opt::
optnew
nouveau-placementopt(
type-id)
new-initializeroptnouveau-placement:
(
liste-d'expressions)
Ainsi, un "placement nouveau" compte comme un new-expression. Rien n'interdit un delete-expression là.
En outre, il s'avère que delete-expression fait exactement les bonnes choses pour nettoyer l'objet malgré la création personnalisée.
5.3.5/6-9
Si la valeur de l'opérande de delete-expression n'est pas une valeur de pointeur nulle, le delete-expression invoquera le destructeur (le cas échéant) pour l'objet ou les éléments du tableau en cours de suppression. ...
Si la valeur de l'opérande de delete-expression n'est pas une valeur de pointeur nulle, le delete-expression appellera --- fonction de désallocation (3.7 .4.2). Sinon, il n'est pas spécifié si la fonction de désallocation sera appelée. [Remarque: La fonction de désallocation est appelée, que le destructeur de l'objet ou un élément du tableau lève une exception. - note de fin]
Lorsque le mot clé
delete
dans un delete-expression est précédé de l'unaire::
, la fonction globale de désallocation est utilisée pour désallouer le stockage.
Donc ::delete buf;
est entièrement équivalent à:
try {
buf->~Buffer();
} catch(...) {
::operator delete(mem);
throw;
}