web-dev-qa-db-fra.com

placement nouveau et supprimer

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?

45
Vink

La bonne méthode est:

buf->~Buffer();
::operator delete(mem);

Vous ne pouvez supprimer qu'avec deleteopérateur ce que vous avez reçu de newopérateur. Si vous appelez directement le operator new, vous devez également appeler directement la fonction operator delete, et doit également appeler manuellement le destructeur.

49
bdonlan

Il existe deux notions distinctes en C++:

  1. Le nouveau/supprimer opérateurs.

  2. 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:

  • Une nouvelle expression surchargée appelle un nouvel opérateur surchargé pour allouer de la mémoire, puis procède à la construction de l'objet.
  • Cependant, une expression de suppression surchargée n'existe pas, en particulier il n'y a pas de "placement-suppression": à la place, vous devez appeler le destructeur manuellement.

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
25
Kerrek SB

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:

  • ::opt deleteexpression-cast
  • ::opt delete [ ]expression-cast

La 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:

  • ::opt newnouveau-placementopt new-type-id _new-initializer_opt
  • ::opt newnouveau-placementopt (type-id)new-initializeropt

nouveau-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;
}
2
aschepler