Est-ce que quelqu'un ici a déjà utilisé le "placement nouveau" de C ++? Si oui, pourquoi? Il me semble que cela ne serait utile que sur du matériel mappé en mémoire.
c++
memory-management
new-operator
placement-new
Geek en chef
la source
la source
p = pt
utiliser et utiliser l'opérateur d'affectationPoint
au lieu de le fairenew(&p) Point(pt)
? Je m'interroge sur les différences entre les deux. Le premier ferait-il appeloperator=
à Point, tandis que le second appellerait constructeur de copiePoint
? mais je ne sais toujours pas pourquoi l'un est meilleur que l'autre.U::operator=
vient d'être appelé.Réponses:
Placement new vous permet de construire un objet en mémoire déjà alloué.
Vous pouvez le faire pour l'optimisation lorsque vous devez construire plusieurs instances d'un objet, et il est plus rapide de ne pas réallouer de la mémoire chaque fois que vous avez besoin d'une nouvelle instance. Au lieu de cela, il peut être plus efficace d'effectuer une seule allocation pour un bloc de mémoire pouvant contenir plusieurs objets, même si vous ne souhaitez pas utiliser tout cela à la fois.
DevX donne un bon exemple :
Vous pouvez également vous assurer qu'il ne peut y avoir aucun échec d'allocation à une certaine partie du code critique (par exemple, dans le code exécuté par un stimulateur cardiaque). Dans ce cas, vous souhaitez allouer de la mémoire plus tôt, puis utiliser le placement nouveau dans la section critique.
Désallocation en placement nouveau
Vous ne devez pas désallouer tous les objets qui utilisent le tampon de mémoire. Au lieu de cela, vous devez supprimer [] uniquement le tampon d'origine. Vous devrez alors appeler manuellement les destructeurs de vos classes. Pour une bonne suggestion à ce sujet, veuillez consulter la FAQ de Stroustrup sur: Y a - t-il une "suppression de placement" ?
la source
delete[]
lechar
tampon d' origine . L'utilisation du placementnew
a mis fin à la durée de vie deschar
objets d' origine en réutilisant leur stockage. Si vous appelez maintenantdelete[] buf
le type dynamique des objets pointés vers, ils ne correspondent plus à leur type statique, vous avez donc un comportement indéfini. Il est plus cohérent d'utiliseroperator new
/operator delete
d'allouer de la mémoire brute destinée à être utilisée par placementnew
.#include <new>
.Nous l'utilisons avec des pools de mémoire personnalisés. Juste un croquis:
Vous pouvez maintenant regrouper des objets dans une seule arène de mémoire, sélectionner un allocateur qui est très rapide mais sans désallocation, utiliser le mappage de mémoire et toute autre sémantique que vous souhaitez imposer en choisissant le pool et en le passant comme argument au placement d'un objet. nouvel opérateur.
la source
allocate()
quelque part?C'est utile si vous voulez séparer l'allocation de l'initialisation. STL utilise placement new pour créer des éléments de conteneur.
la source
Je l'ai utilisé dans la programmation en temps réel. Nous ne voulons généralement pas effectuer d'allocation dynamique (ou de désallocation) après le démarrage du système, car il n'y a aucune garantie de la durée de cette opération.
Ce que je peux faire, c'est préallouer une grande partie de la mémoire (suffisamment grande pour contenir tout ce dont la classe peut avoir besoin). Ensuite, une fois que j'ai compris au moment de l'exécution comment construire les choses, le placement nouveau peut être utilisé pour construire des objets là où je les veux. Je sais que je l'ai utilisé dans une situation pour aider à créer un tampon circulaire hétérogène .
Ce n'est certainement pas pour les âmes sensibles, mais c'est pourquoi ils en font un peu la syntaxe.
la source
Je l'ai utilisé pour construire des objets alloués sur la pile via alloca ().
fiche sans vergogne: j'ai blogué à ce sujet ici .
la source
boost::array
. Pouvez-vous développer un peu cela?Geek en chef: BINGO! Vous l'avez totalement compris - c'est exactement ce pour quoi il est parfait. Dans de nombreux environnements embarqués, les contraintes externes et / ou le scénario d'utilisation globale obligent le programmeur à séparer l'allocation d'un objet de son initialisation. Ensemble, C ++ appelle cette "instanciation"; mais chaque fois que l'action du constructeur doit être explicitement invoquée SANS allocation dynamique ou automatique, le placement nouveau est le moyen de le faire. C'est également le moyen idéal pour localiser un objet C ++ global qui est épinglé à l'adresse d'un composant matériel (E / S mappées en mémoire), ou pour tout objet statique qui, pour une raison quelconque, doit résider à une adresse fixe.
la source
Je l'ai utilisé pour créer une classe Variant (c'est-à-dire un objet qui peut représenter une valeur unique qui peut être l'un d'un certain nombre de types différents).
Si tous les types de valeurs pris en charge par la classe Variant sont des types POD (par exemple, int, float, double, bool), une union de style C balisée est suffisante, mais si vous voulez que certains types de valeurs soient des objets C ++ ( par exemple std :: string), la fonction d'union C ne fera pas l'affaire, car les types de données non POD ne peuvent pas être déclarés comme faisant partie d'une union.
Donc, à la place, j'alloue un tableau d'octets suffisamment grand (par exemple sizeof (the_largest_data_type_I_support)) et j'utilise placement new pour initialiser l'objet C ++ approprié dans cette zone lorsque le Variant est défini pour contenir une valeur de ce type. (Et l'emplacement doit être supprimé au préalable lors de la transition d'un autre type de données non POD, bien sûr)
la source
new
pour initialiser sa sous-classe non-POD. Ref: stackoverflow.com/a/33289972/2757035 Réinventer cette roue en utilisant un tableau d'octets arbitrairement grand est un morceau acrobatique impressionnant mais semble totalement inutile, Alors, qu'ai-je manqué? :)Placement new est également très utile lors de la sérialisation (par exemple avec boost :: serialization). En 10 ans de c ++, ce n'est que le deuxième cas pour lequel j'ai eu besoin d'un nouveau placement (troisième si vous incluez des interviews :)).
la source
Il est également utile lorsque vous souhaitez réinitialiser des structures globales ou allouées statiquement.
L'ancienne façon C utilisait
memset()
pour définir tous les éléments à 0. Vous ne pouvez pas le faire en C ++ en raison de vtables et de constructeurs d'objets personnalisés.J'utilise donc parfois ce qui suit
la source
Je pense que cela n'a été mis en évidence par aucune réponse, mais un autre bon exemple et utilisation pour le nouveau placement est de réduire la fragmentation de la mémoire (en utilisant des pools de mémoire). Ceci est particulièrement utile dans les systèmes embarqués et à haute disponibilité. Dans ce dernier cas, c'est particulièrement important car pour un système qui doit fonctionner 24/365 jours, il est très important de ne pas avoir de fragmentation. Ce problème n'a rien à voir avec une fuite de mémoire.
Même lorsqu'une très bonne implémentation malloc est utilisée (ou une fonction de gestion de mémoire similaire), il est très difficile de gérer la fragmentation pendant longtemps. À un moment donné, si vous ne gérez pas intelligemment les appels de réservation / libération de mémoire, vous pourriez vous retrouver avec beaucoup de petites lacunes qui sont difficiles à réutiliser (attribuer à de nouvelles réservations). Ainsi, l'une des solutions utilisées dans ce cas consiste à utiliser un pool de mémoire pour allouer au préalable la mémoire pour les objets d'application. Après chaque fois que vous avez besoin de mémoire pour un objet, vous utilisez simplement le nouvel emplacement pour créer un nouvel objet sur la mémoire déjà réservée.
De cette façon, une fois que votre application démarre, vous disposez déjà de toute la mémoire nécessaire. Toutes les nouvelles réservations / versions de mémoire vont aux pools alloués (vous pouvez avoir plusieurs pools, un pour chaque classe d'objet différente). Aucune fragmentation de la mémoire ne se produit dans ce cas car il n'y aura pas de lacunes et votre système peut fonctionner pendant de très longues périodes (années) sans souffrir de fragmentation.
J'ai vu cela dans la pratique spécialement pour le VxWorks RTOS car son système d'allocation de mémoire par défaut souffre beaucoup de la fragmentation. Ainsi, l'allocation de mémoire via la méthode standard new / malloc était fondamentalement interdite dans le projet. Toutes les réservations de mémoire doivent aller dans un pool de mémoire dédié.
la source
Il est en fait nécessaire d'implémenter tout type de structure de données qui alloue plus de mémoire que le minimum requis pour le nombre d'éléments insérés (c'est-à-dire autre chose qu'une structure liée qui alloue un nœud à la fois).
Prenez des conteneurs aiment
unordered_map
,vector
oudeque
. Tous ces éléments allouent plus de mémoire que ce qui est minimalement requis pour les éléments que vous avez insérés jusqu'à présent pour éviter d'exiger une allocation de segment de mémoire pour chaque insertion unique. Prenons l'vector
exemple le plus simple.Quand vous faites:
... qui ne construit pas réellement mille Foos. Il leur alloue / réserve simplement de la mémoire. Si vous
vector
n'utilisiez pas le placement nouveau ici, ce serait une construction par défautFoos
partout et vous devrez invoquer leurs destructeurs même pour les éléments que vous n'avez même jamais insérés en premier lieu.Allocation! = Construction, Libération! = Destruction
D'une manière générale, pour implémenter de nombreuses structures de données comme ci-dessus, vous ne pouvez pas traiter l'allocation de mémoire et la construction d'éléments comme une chose indivisible, et vous ne pouvez pas non plus traiter la libération de mémoire et la destruction d'éléments comme une chose indivisible.
Il doit y avoir une séparation entre ces idées pour éviter d'invoquer inutilement des constructeurs et des destructeurs à gauche et à droite, et c'est pourquoi la bibliothèque standard sépare l'idée de
std::allocator
(qui ne construit pas ou ne détruit pas les éléments lorsqu'elle alloue / libère de la mémoire *) loin de les conteneurs qui l'utilisent qui construisent manuellement des éléments en utilisant placement new et détruisent manuellement des éléments en utilisant des invocations explicites de destructeurs.Donc, de toute façon, j'ai tendance à l'utiliser beaucoup car j'ai écrit un certain nombre de conteneurs C ++ standard à usage général qui ne pouvaient pas être construits en fonction des conteneurs existants. Parmi eux, une petite implémentation vectorielle que j'ai construite il y a quelques décennies pour éviter les allocations de tas dans les cas courants, et un tri efficace en mémoire (n'alloue pas un nœud à la fois). Dans les deux cas, je ne pouvais pas vraiment les implémenter en utilisant les conteneurs existants, et j'ai donc dû
placement new
éviter d'utiliser des constructeurs et des destructeurs superflus sur des choses inutiles à gauche et à droite.Naturellement, si vous travaillez avec des allocateurs personnalisés pour allouer des objets individuellement, comme une liste gratuite, vous voudrez également généralement utiliser
placement new
, comme ceci (exemple de base qui ne dérange pas avec la sécurité d'exception ou RAII):la source
C'est utile si vous construisez un noyau - où placez-vous le code du noyau que vous lisez à partir du disque ou de la page? Vous devez savoir où aller.
Ou dans d'autres circonstances très rares, comme lorsque vous avez beaucoup de place allouée et que vous souhaitez placer quelques structures les unes derrière les autres. Ils peuvent être compressés de cette façon sans avoir besoin de l'opérateur offsetof (). Il existe cependant d'autres astuces pour cela.
Je crois également que certaines implémentations STL utilisent le placement new, comme std :: vector. Ils allouent de la place pour 2 ^ n éléments de cette façon et n'ont pas besoin de toujours se réallouer.
la source
Il est utilisé par
std::vector<>
carstd::vector<>
alloue généralement plus de mémoire qu'il n'y en aobjects
dans levector<>
.la source
Je l'ai utilisé pour stocker des objets avec des fichiers mappés en mémoire.
L'exemple spécifique était une base de données d'images qui traitait un très grand nombre de grandes images (plus que ce qui pouvait tenir en mémoire).
la source
Je l'ai vu utilisé comme un léger hack de performance pour un pointeur de "type dynamique" (dans la section "Sous le capot"):
la source
void*
prend 8 octets. Il est un peu idiot de pointer un octetvoid*
sur un octetbool
. Mais il est tout à fait possible de superposer lebool
sur levoid*
, un peu comme ununion { bool b; void* v }
. Vous avez besoin d'un moyen de savoir que ce que vous appelez unvoid*
est en fait unbool
(ou unshort
, ou unfloat
, etc.). L'article auquel j'ai lié décrit comment procéder. Et, pour répondre à la question d'origine, le placementnew
est la fonctionnalité utilisée pour créer unbool
(ou un autre type) où unvoid*
est attendu, (les transtypages sont utilisés pour obtenir / modifier la valeur ultérieurement).Je l'ai utilisé pour créer des objets basés sur la mémoire contenant des messages reçus du réseau.
la source
Généralement, le placement nouveau est utilisé pour se débarrasser du coût d'allocation d'un «nouveau normal».
Un autre scénario où je l'ai utilisé est un endroit où je voulais avoir accès au pointeur sur un objet qui devait encore être construit, pour implémenter un singleton par document.
la source
Il peut être utile lors de l'utilisation de la mémoire partagée, entre autres utilisations ... Par exemple: http://www.boost.org/doc/libs/1_51_0/doc/html/interprocess/synchronization_mechanisms.html#interprocess.synchronization_mechanisms.conditions. conditions_anonymous_example
la source
Le seul endroit que j'ai rencontré est dans des conteneurs qui allouent un tampon contigu, puis le remplissent d'objets selon les besoins. Comme mentionné, std :: vector peut le faire, et je sais que certaines versions de MFC CArray et / ou CList l'ont fait (parce que c'est là que je l'ai rencontré pour la première fois). La méthode de surallocation du tampon est une optimisation très utile, et le placement nouveau est à peu près le seul moyen de construire des objets dans ce scénario. Il est également parfois utilisé pour construire des objets dans des blocs de mémoire alloués en dehors de votre code direct.
Je l'ai utilisé à un titre similaire, bien qu'il ne revienne pas souvent. C'est un outil utile pour la boîte à outils C ++, cependant.
la source
Les moteurs de script peuvent l'utiliser dans l'interface native pour allouer des objets natifs à partir de scripts. Voir Angelscript (www.angelcode.com/angelscript) pour des exemples.
la source
Voir le fichier fp.h dans le projet xll à http://xll.codeplex.com Il résout le problème de "chumminess injustifié avec le compilateur" pour les tableaux qui aiment transporter leurs dimensions avec eux.
la source
Voici l'utilisation de tueur pour le constructeur sur place C ++: l'alignement sur une ligne de cache, ainsi que d'autres pouvoirs de 2 limites. Voici mon algorithme d'alignement de pointeur ultra-rapide à n'importe quelle puissance de 2 frontières avec 5 instructions de cycle unique ou moins :
Maintenant, cela ne vous fait-il pas sourire (:-). I ♥♥♥ C ++ 1x
la source