12
votes

Appelle-t-il de la suppression du résultat d'un placement supprimé qui a utilisé l'opérateur neuf d'accord?

Si je fais

struct MyStruct { ~MyStruct() { } };

void *buffer = operator new(1024);
MyStruct *p = new(buffer) MyStruct();
// ...
delete p;     //    <---------- is this okay?


2 commentaires

Existe-t-il une différence dans la représentation de la mémoire résultante entre la façon dont vous avez alloué et construit l'objet contre le faire en une étape? Si oui, alors la suppression échouera probablement. Si non, alors la suppression réussira probablement. La question revient donc à la manière dont la standard spécifie le fonctionnement de nouveau , pas Supprimer .


J'ai testé cela avec GCC + Valgrind - et aucune erreur n'a été rapportée.


3 Réponses :


12
votes

[révisé] Ce n'est généralement pas d'accord, et vous ne pouvez généralement que Supprimer CODE> quelque chose qui a été obtenu avec l'expression correspondante nouvelle code>. Sinon, il vous appartient de garantir que la fonction de translocation correspond à la fonction d'allocation (afin d'utiliser :: Supprimer p; code> serait la solution générale plus sûre, en supposant que votre opérateur original nouveau code> était dans l'espace de noms global). En particulier, lorsque la classe en question (ou l'une de ses classes dérivées) surchargez nouvel opérateur code> vous devez faire attention.

puisque vous utilisez une nouvelle expression de placement pour créer * P code>, et puisqu'il n'y a pas de "expression de placement-suppression", vous devez détruire l'objet manuellement, puis relâcher la mémoire: p>

p->~MyStruct();

operator delete(buffer);


9 commentaires

En dehors de Knee-Serk "C'est étrange et lié à la mémoire, il doit donc être ub", quelles preuves avez-vous pour cela?


@Mankarse: Supprimer p serait potentiellement invoquer muntructeur :: Opérateur Supprimer , qui peut faire quelque chose de tout à fait inapproprié, WHERAS :: Opérateur Supprimer est réellement appelé pour.


Vrai, mais cela ne signifie pas que vous pouvez seulement Supprimer quelque chose qui a été obtenu avec la plaine nouvelle expression . Cela peut également travailler dans ces situations où vous savez que muntructeur :: l'opérateur Supprimer n'existe pas. (Bien que j'accepte que cela fait d'utiliser Supprimer un peu dangereux et en fait probablement une mauvaise idée dans la plupart des situations).


@Mankarse: Aussi 5.3.5 / 2: "La valeur de l'opérande de Supprimer peut être [...] un pointeur sur un objet non-tableau créé par une nouvelle expression précédente [.. .]. Sinon, le comportement n'est pas défini. "


@Mankarse: "comportement non défini" fait bien sûr "fait exactement ce que vous pensez que cela devrait faire". (Donc, cela pourrait fonctionner dans un cas donné, mais ce n'est généralement pas correct.)


Le placement nouveau est une forme de nouvelle expression (voir ma réponse ci-dessous) et une durée de vie d'un objet ne commence que lorsque son initialisation est terminée ( [basic.life] / 1 ) , donc placement nouveau fait crée des objets.


Hmm, je ne suis pas convaincu de la réponse encore ... @kerreksb: Pourriez-vous élargir ce qui se passerait si opérateur Nouveau Opérateur Supprimer était pas surchargé? Y aurait-il des garanties alors?


@Mehrdad: Mankarse a eu raison. S'il n'y a pas d'opérateur surchargé Supprimer dans la classe, c'est le type de * p (le type dynamique si le destructeur est virtuel), puis Supprimer p; Recherchera la fonction dans l'espace de noms global. En supposant que votre opérateur original nouveau était également dans l'espace de noms global, cela devrait être correct.


@Mehrdad: Mais notez que cette conception n'est pas sûre: en ajoutant quelque chose au cours plus tard, quelqu'un pourrait casser votre code à distance et vous ne sauriez jamais. Vous pouvez vous épargner tout ce trouble en faisant la bonne chose. (La réelle la bonne chose de droite n'est jamais d'épeler "nouveau" dans le code de la clientèle de toute façon et utilisez plutôt quelque chose comme tous les allocateurs et Allocate_shared etc.)



13
votes

Supprimer p est équivalent à xxx

sauf si muntruct a une alternative l'opérateur supprime , donc votre exemple doit être bien définie avec la sémantique attendues

[expr.delete] / 2 états:.

La valeur de l'opérande de Suppr peut être ... un pointeur sur un objet non-arrayé créé par un précédent nouvelle expression . .

placement nouveau est un type de nouvelle expression . [expr.new] / 1 :

nouvelle expression :
      :: OPT Nouveau Nouvel Placement OPT Nouvelle-Type-ID NOUVEAU- Initializer opt
    :: opt nouveau nouveau placement opt ( ID de type ) nouvel initialiseur opt

Supprimer est défini comme un appel au destructeur de l'objet, puis un appel à la fonction de distribution pour la mémoire. [expr.delete] / 6,7 :

... La Supprimer-expression invoquera le destructeur (le cas échéant) pour l'objet ...

... L'expression Supprimer appellera une fonction de distribution ...

Tant que la fonction de distribution correspond à la fonction d'allocation (qu'elle doit, tant que vous n'avez pas surchargé EXPLICATION pour votre classe), cela devrait-il tous être bien définie.


2 commentaires

Hmm. Vous devez également noter que la mise en œuvre n'est pas autorisée à ajouter un remplissage entre après la valeur de retour de opérateur nouveau . (Ce n'est pas; C ++ 11 §5.3.4 / 10.) Même ainsi, je me sens toujours mal à l'égard de cela. Il ne semble pas y avoir un cas d'utilisation inévitable depuis dynamic_cast () vous obtient le pointeur le plus dérivé même lorsque vous ne connaissez pas le type dynamique.


Le consensus uniforme est que le passage de la valeur de retour de l'emplacement Nouveau sur Supprimer n'est pas OK après tout , le placement nouveau n'exige pas que la mémoire ait été obtenue de :: opérateur nouveau () en premier lieu. Le reste de cette réponse s'effondre immédiatement, avec ce malentendu fondamental supprimé.



0
votes

Non, vous ne devez généralement pas appeler Supprimer (dans certains cas comme lorsque la suppression de l'opérateur est superficielle, cela peut être ok).

char *buffer = new char[1024];
MyStruct *p = new(buffer) MyStruct(); //placement new "i'm just calling constructor"
p->~MyStruct();  //destroy the object.

//delete p; // WHAT? even if you override operator delete it may be opaque to reader.
delete [] buffer; // THIS DELETE IS OK (if you have first destroyed MyStruct)


0 commentaires