2017-09-28 6 views
0

Est-ce que quelqu'un connaît une procédure de suppression C++ qui est sûre à la fois pour le placement nouveau et pour le nouveau standard?Supprimer pour le placement ou le non-placement nouveau

Object* regular = new Object(); 
delete_procedure(regular); 

void* buf = ::new(sizeof(Object)); 
Object* placement = new(buf) Object(); 
delete_procedure(placement); 

Cette fonction semble fonctionner, mais je ne peux pas savoir si elle est réellement sans danger pour les deux cas (c'est la méthode standard de suppression de placement nouveau).

delete_procedure(Object* obj){ // Not sure if safe for regular new 
    obj->~Object(); 
    ::delete(obj); 
} 
+0

Au cas où cela vous intéresse: Pourquoi ai-je besoin de cela? J'écris un ramasse-miettes pour les structures de données concurrentes et certains objets gérés par le GC peuvent être effectivement dimensionnés de manière variable par placement nouveau pour l'efficacité (par exemple métadonnées suivies d'une chaîne c), certains objets pourraient ne pas l'être, et je préférerais sur l'utilisateur pour garder une trace de la différence. – Joe

+2

Aucune procédure de ce type ne peut exister, car vous devez faire correspondre 'delete' avec le' new' correspondant, et de plus, il n'y a pas d '"expression de suppression de placement". –

+0

Toujours utiliser le placement nouveau? –

Répondre

3

Je ne crois pas qu'il existe une façon unifiée de le faire sans introduire des drapeaux supplémentaires pour marquer les choses. La philosophie de C++ est «vous devez garder une trace de la façon dont vous avez alloué les choses et vous assurer qu'ils sont tous détruits correctement», et cela à des fins d'efficacité. Pensez à delete[] par rapport à delete. Il pourrait y avoir un mot-clé unifié delete qui nettoie tout bloc de mémoire, mais qui entraînerait une perte de performance en raison du coût de la détermination de la routine de désallocation qui doit être appelée. Ou pensez à des destructeurs virtuels: C++ pourrait dire que delete fait toujours une sorte d'introspection de type, mais cela ajouterait un surcoût à tous les types d'objets, en grande partie inutilement. La philosophie de C++ est «ne payez pas pour ce que vous n'utilisez pas», et si vous voulez construire quelque chose d'unifié, vous aurez besoin de mettre en place une infrastructure supplémentaire pour suivre les types de routine de suppression dont vous avez besoin faire.

+0

Je serais surpris s'il y a une plate-forme où les drapeaux sont nécessaires, à condition que l'expression d'allocation est fixée. Notez qu'il s'agit d'une question pratique et non d'une question d'avocat. Quoi qu'il en soit, cette réponse est un commentaire, exprimant votre conviction et quelques conseils sur ce qu'il faut penser, pas une réponse, donc, signalé comme tel. –

1

Non, en raison des tableaux et des détails. Bien sûr, parce que ces problèmes n'ont probablement pas d'importance dans votre base de code.

Tous les suppressions sont de type suppressions spécifiques, ou:

Le comportement de la mise en œuvre de la bibliothèque standard de cette fonction est définie, sauf si ptr est un pointeur nul ou est un pointeur précédemment obtenu à partir de la mise en œuvre de la bibliothèque standard de opérateur new (taille_t) ou opérateur new (taille_t, std :: nothrow_t).

de http://en.cppreference.com/w/cpp/memory/new/operator_delete

Ce moyen de passage mémoire allouée par new[](size_t)-delete(void*) est UB, et new[](size_t)-delete[](void*) est UB. Deuxièmement, les utilisateurs sont libres de remplacer new/delete sur une base globale ou par classe.


Troisième:

void delete_procedure(Object* obj){ 
    obj->~Object(); 
    ::operator delete((void*)obj); 
} 

vous voulez lancer d'annuler là et utiliser l'opérateur pointeur supprimer.


Forth, le pointeur est passé à delete_procedure doit être le pointeur généré par new T ou ::new(::new(sizeof(T))) T; il ne peut pas s'agir d'un pointeur vers un sous-objet dérivé, car sa valeur en tant que void* peut différer.

Si T* pt est castable dynamique, dynamic_cast<void*>(pt) va reconstruire cela void*. C'est donc moins une barrière qu'il n'y paraît. N'oubliez pas de faire le casting avant de détruire l'objet!

void delete_procedure_dynamic(Object* obj){ 
    void* ptr=dynamic_cast<void*>(obj); 
    obj->~Object(); 
    ::operator delete(ptr); 
} 

et d'utiliser SFINAE/tag dispatching pour répartir entre les versions dynamique et non-dynamique. Cinquième, les types d'alignement élevés ont besoin de travail à manipuler. Ils utilisent différents nouveaux et suppriment. Je ne suis pas certain de l'interaction entre le casting dynamique et les types dérivés alignés, mais vous n'avez probablement pas besoin de vous en soucier.


Mais new T appels ::new(size_t)`` then constructs an object there. opérateur supprimer (void *) must be fed memory produced by :: nouveau (size_t) . Destroying an object creates by nouveau T is legal via. ~ T() `. Donc, je ne vois rien de fondamentalement brisé. En pratique, ce que je pourrais faire est remplacer le "normal" nouveau pour suivre le même modèle que votre nouveau stockage supplémentaire (et allouer le bloc via new(size_t)) pour garder les choses simples. Vous êtes en territoire arcanique, pourquoi ne pas rendre les choses uniformes.

+0

Pourrait être encore mieux en mentionnant 'dynamic_cast ' pour obtenir l'objet complet. Oh, et la sécurité d'exception. Je pense que l'OP a publié un pseudo-code, je ne vois pas comment cela pourrait se compiler. –