74 votes

Pourquoi remplacerait-on par défaut les nouveaux opérateurs et les opérateurs de suppression?

Pourquoi devrait serait-on remplacer la valeur par défaut de l'opérateur new et delete avec une coutume new et delete opérateurs?

C'est dans le prolongement de la Surcharge new et delete dans le très éclairant C++ FAQ:
La surcharge d'opérateur.

Un suivi de l'entrée à cette FAQ:
Comment dois-je écrire ISO C++ standard conforme personnalisée new et delete opérateurs?

Remarque: La réponse est basée sur les enseignements de Scott Meyers Plus Efficace C++.
(Note: Ceci est destiné à être une entrée à Débordement de Pile du C++ FAQ. Si vous voulez une critique de l'idée de fournir une FAQ dans ce formulaire, puis de la poster sur meta qui a commencé tout cela serait l'endroit pour le faire. Les réponses à cette question sont surveillés en C++ salon, où la FAQ idée a commencé à en premier lieu, de sorte que votre réponse est très probablement le faire lire par ceux qui sont venus avec l'idée.)

76voto

Alok Save Points 115848

On peut essayer de remplacer new et delete des opérateurs pour un certain nombre de raisons, à savoir:

Pour Détecter Les Erreurs D'Utilisation:

Il y a un certain nombre de façons dont l'utilisation incorrecte de l' new et delete peut conduire à la redoutable bête de Comportement Indéfini Et les fuites de Mémoire. Respectifs des exemples de chacun sont les suivants:
À l'aide de plus d'un delete sur newed de mémoire et de ne pas appeler delete sur la mémoire allouée à l'aide d' new.
Un opérateur surchargé new pouvez tenir une liste des adresses allouées et l'opérateur surchargé delete pouvez supprimer des adresses de la liste, alors il est facile de détecter les erreurs d'utilisation.

De même, une variété de la programmation, des erreurs peuvent conduire à des dépassements de données(écriture au-delà de la fin d'un bloc alloué) et de flux de données(par écrit avant le début d'un bloc alloué).
Un opérateur Surchargé new peut allouer des blocs et de mettre connu des modèles d'octets ("signatures") avant et après la mémoire disponible pour les clients. La surcharge de l'opérateur supprime pouvez vérifier pour voir si les signatures sont toujours intacts. Ainsi, en vérifiant si ces signatures ne sont pas intacts, il est possible de déterminer qu'un dépassement ou sous-exécution s'est produite au cours de la durée de vie du bloc alloué, et l'opérateur delete peut se connecter que fait, avec la valeur de la délinquance pointeur, contribuant ainsi à assurer une bonne information de diagnostic.


Pour Améliorer l'Efficacité(de la vitesse et de mémoire):

L' new et delete opérateurs fonctionnent raisonnablement bien pour tout le monde, mais de façon optimale pour personne. Ce problème découle du fait qu'ils sont conçus pour un usage général. Ils ont pour accueillir les schémas de répartition allant de l'allocation dynamique de quelques pâtés de maisons qui existent pendant la durée du programme permanent de l'allocation et la libération d'un grand nombre d'objets éphémères. Finalement, l'opérateur new et l'opérateur delete livrés avec des compilateurs de prendre un moyen-de-la-route de la stratégie.

Si vous avez une bonne compréhension de votre programme de dynamique de la mémoire des modèles d'utilisation, vous pouvez souvent trouver que des versions personnalisées de nouvel opérateur et l'opérateur delete surpasser (plus rapide dans la performance, ou nécessitent moins de mémoire jusqu'à 50%)de la valeur par défaut. Bien sûr, sauf si vous êtes sûr de ce que vous faites, ce n'est pas une bonne idée de le faire(n'essayez même pas si vous ne comprenez pas les complexités impliquées).


Pour Collecter Des Statistiques D'Utilisation:

Avant de penser à remplacer new et delete pour l'amélioration de l'efficacité comme mentionné dans la question #2, Vous devez recueillir des informations sur la façon dont votre application/le programme utilise l'allocation dynamique. Vous pouvez recueillir des informations sur:
La Distribution de l'allocation des blocs,
La Distribution des durées de vie,
Afin d'allocations(FIFO ou LIFO ou aléatoire),
Comprendre les habitudes d'utilisation des changements sur une période de temps,la quantité maximale de mémoire dynamique utilisée etc.

Aussi, il est parfois nécessaire de recueillir des informations d'utilisation tels que:
Compter le nombre de dynamiquement les objets d'une classe,
Limiter le nombre d'objets créés à l'aide de l'allocation dynamique etc.

Toutes ces informations peuvent être collectées par le remplacement de la coutume new et delete et en ajoutant le diagnostic mécanisme de collecte de l'surchargé new et delete.


Pour compenser la sous-optimale alignement de la mémoire en new:

De nombreuses architectures informatiques nécessitent que les données de types particuliers d'être placé dans la mémoire à certains types d'adresses. Par exemple, une architecture peut exiger que les pointeurs de se produire à des adresses qui sont un multiple de quatre (c'est à dire, de quatre octets alignés) ou double doit se produire à des adresses qui sont un multiple de huit (c'est à dire, de huit octets aligné). Le défaut de suivre ces contraintes peuvent conduire à du matériel exceptions au moment de l'exécution. D'autres architectures sont plus indulgents, et peut lui permettre de fonctionner à travers la réduction de la performance.L'opérateur new fournis avec certains compilateurs ne garantissent pas les huit octets de l'alignement dynamique les allocations de doubles. Dans de tels cas, le remplacement de l'opérateur par défaut new avec celle qui garantit à huit octets alignement pourrait donner une importante augmentation de la performance du programme et peut être une bonne raison pour remplacer new et delete opérateurs.


Pour cluster liés à des objets à proximité l'un de l'autre:

Si vous savez que notamment les structures de données sont généralement utilisés ensemble et que vous souhaitez réduire la fréquence des défauts de page lorsque l'on travaille sur les données, il peut être judicieux de créer un segment séparé pour les structures de données de sorte qu'ils sont regroupés sur quelques pages que possible. personnalisé Placement versions d' new et delete peut rendre possible la réalisation d'un tel regroupement.


Pour obtenir non conventionnelles comportement:

Parfois, vous voulez opérateurs new et delete pour faire quelque chose que le compilateur fourni des versions n'offrent pas.
Par exemple: Vous pourriez écrire une coutume de l'opérateur delete qui remplace désallocation de la mémoire avec des zéros afin d'augmenter la sécurité des données d'application.

14voto

Jerry Coffin Points 237758

Tout d'abord, il y a vraiment un certain nombre de différents new et delete opérateurs (un nombre arbitraire, vraiment).

Tout d'abord, il y a ::operator new, ::operator new[], ::operator delete et ::operator delete[]. Deuxièmement, quelle que soit la classe X, il y a X::operator new, X::operator new[], X::operator delete et X::operator delete[].

Entre eux, c'est beaucoup plus courant de surcharge de la classe spécifique des opérateurs que les opérateurs globaux -- il est assez commun pour l'utilisation de la mémoire d'une classe particulière de suivre une assez spécifique de modèle, vous pouvez écrire des opérateurs qui fournissent des améliorations substantielles sur les valeurs par défaut. Il est généralement beaucoup plus difficile de prévoir l'utilisation de la mémoire presque exactement ou, plus spécifiquement, sur une base globale.

C'est probablement aussi la peine de mentionner que même si operator new et operator new[] sont séparés les uns des autres (de même pour n'importe quel X::operator new et X::operator new[]), il n'y a pas de différence entre les exigences pour les deux. On va être invoquées pour allouer un objet unique, et l'autre pour allouer un tableau d'objets, mais chacun reste juste reçoit une quantité de mémoire nécessaire, et doit renvoyer l'adresse d'un bloc de mémoire (au moins) que les grands.

Parlant des exigences, il est probablement judicieux de revoir les autres exigences1: les opérateurs globaux doivent être véritablement mondial -- vous ne pouvez pas mettre l'un à l'intérieur d'un espace de noms ou de prendre un statique dans une unité de traduction. En d'autres termes, il n'existe que deux niveaux qui surcharge peut avoir lieu: une classe spécifique de la surcharge globale ou de surcharge. Dans l'entre-deux points tels que "toutes les classes dans l'espace de noms X" ou "toutes les attributions dans l'unité de traduction de Y" ne sont pas autorisés. La classe des opérateurs spécifiques sont nécessaires pour être static - mais vous n'êtes pas vraiment nécessaire de les déclarer comme statique -- ils vont être statique si vous avez explicitement déclarer static ou pas. Officiellement, les opérateurs globaux beaucoup de retour de la mémoire aligné de sorte qu'il peut être utilisé pour un objet de type quelconque. Officieusement, il y a un peu wiggle-chambre à un égard: si vous recevez une demande d'un petit bloc (par exemple, 2 octets) vous avez seulement besoin de fournir de la mémoire aligné pour un objet de cette taille, depuis la tentative de stocker quoi que ce soit plus il y aurait de conduire à un comportement indéfini de toute façon.

Ayant couvert ces préliminaires, nous allons revenir à la question initiale sur pourquoi vous voulez à la surcharge de ces opérateurs. Tout d'abord, je dois souligner que les raisons pour surcharger les opérateurs mondiaux ont tendance à être considérablement différents de la des raisons de surcharge de la classe des opérateurs spécifiques.

Comme il est plus courant, je vais vous parler de la classe des opérateurs spécifiques à la première. La raison principale de la classe de gestion de la mémoire est la performance. Cela vient généralement dans les deux (ou les deux) des deux formes: soit l'amélioration de la vitesse ou de la réduction de la fragmentation. La vitesse est améliorée par le fait que le gestionnaire de mémoire sera seulement traiter avec des blocs d'une taille particulière, de sorte qu'il peut retourner à l'adresse d'un bloc libre plutôt que de passer tout le temps de vérifier si un bloc est assez grand, la division d'un bloc en deux si elle est trop grande, etc. La Fragmentation est réduit de (surtout) de la même manière, par exemple, de pré-allocation d'un bloc assez grand pour N objets donne exactement l'espace nécessaire pour N objets; allouer un objet de mémoire allouer exactement l'espace d'un objet, et pas un seul octet de plus.

Il y a une grande variété de raisons pour surcharger la mémoire globale de gestion des opérateurs. Beaucoup de ces derniers sont orientés vers le débogage ou de l'instrumentation, telles que le suivi de la quantité totale de mémoire requises par une application (par exemple, en préparation pour le portage d'un système embarqué), ou le débogage des problèmes de mémoire en montrant l'inadéquation entre l'allocation et la libération de la mémoire. Une autre stratégie consiste à allouer de la mémoire supplémentaire avant et après les limites de chaque bloc, et l'écriture des modèles uniques dans ces domaines. À la fin de l'exécution (et éventuellement d'autres fois aussi), ces domaines sont examinés pour voir si le code est écrit en dehors de la alloués limites. Pourtant, une autre est de tenter d'améliorer la facilité d'utilisation en automatisant au moins certains aspects de l'allocation de mémoire ou de suppression, comme avec un automatisée garbage collector.

Une non-valeur par défaut global d'allocation peut être utilisé pour améliorer les performances. Un cas typique serait de remplacer une valeur par défaut de l'allocateur qui était juste lent en général (p. ex., au moins certaines versions de MS VC++ autour de 4.x pourrait appeler le système HeapAlloc et HeapFree fonctions pour chaque affectation, l'opération de suppression). Une autre possibilité que j'ai vu, dans la pratique, a eu lieu sur les processeurs Intel lors de l'utilisation de l'ESS opérations. Ceux-ci opèrent sur des données 128-bit. Alors que les opérations de travail, indépendamment de l'alignement, la vitesse est améliorée lorsque les données sont alignées à 128 bits limites. Certains compilateurs (par exemple, MS VC++ nouveau2) n'ont pas nécessairement appliquées à l'alignement que plus de limite, de sorte que même si le code à l'aide de la valeur par défaut de l'allocateur, en remplacement de l'allocation pourrait fournir une substantielle amélioration de la vitesse pour ces opérations.


  1. La plupart des besoins sont couverts dans le §3.7.3 et l'article 18.4 de la norme C++ (ou §3.7.4 et §de 18,6 dans C++0x, à moins que de N3291).
  2. Je me sens obligé de souligner que je n'ai pas l'intention de s'en prendre à Microsoft compilateur -- je doute qu'il a un nombre inhabituel de ces problèmes, mais il m'arrive de l'utiliser beaucoup, donc j'ai tendance à être tout à fait conscient de ses problèmes.

6voto

Luc Danton Points 21421

De nombreuses architectures informatiques nécessitent que les données de types particuliers d'être placé dans la mémoire à certains types d'adresses. Par exemple, une architecture peut exiger que les pointeurs de se produire à des adresses qui sont un multiple de quatre (c'est à dire, de quatre octets alignés) ou double doit se produire à des adresses qui sont un multiple de huit (c'est à dire, de huit octets aligné). Le défaut de suivre ces contraintes peuvent conduire à du matériel exceptions au moment de l'exécution. D'autres architectures sont plus indulgents, et peut lui permettre de fonctionner à travers la réduction de la performance.

Pour clarifier: si une architecture nécessite , par exemple, que double des données de huit octets alignés, alors il n'y a rien à optimiser. Tout type d'allocation dynamique de la taille appropriée (par exemple, malloc(size), operator new(size), operator new[](size), new char[size]size >= sizeof(double)) est garanti pour être correctement alignées. Si une mise en œuvre n'est pas de faire de cette garantie, il n'est pas conforme. Évolution operator new à faire "la chose" dans ce cas serait une tentative de "fixation" de la mise en œuvre, non pas d'une optimisation.

D'autre part, certaines architectures différentes (ou toutes) sortes d'alignement pour un ou plusieurs types de données, mais de fournir des différentes garanties de performance en fonction de l'alignement de ces mêmes types. Une mise en œuvre peut alors le retour de la mémoire (encore une fois, en supposant une demande de taille appropriée) qui est sous-aligné de façon optimale, et encore être en conformité. C'est ce que l'exemple est d'environ.

4voto

Russell Borogove Points 8423

Relatif aux statistiques d'utilisation: budgétisation par sous-système. Par exemple, dans un jeu en console, vous pouvez réserver une fraction de la mémoire pour la géométrie du modèle 3D, pour les textures, pour les sons, pour les scripts de jeu, etc. Les allocateurs personnalisés peuvent baliser chaque allocation par sous-système et émettre un avertissement lorsque les budgets individuels sont dépassés.

4voto

Mark Points 41

L'opérateur new fournis avec certains compilateurs ne garantit pas de huit octets à l'alignement pour les allocations dynamiques de doubles.

Citation, s'il vous plaît. Normalement, la valeur par défaut nouvel opérateur est seulement un peu plus complexe qu'un malloc wrapper, qui, par la norme, les retours de la mémoire qui est correctement alignés pour TOUT type de données que l'architecture cible prend en charge.

Non pas que je suis en train de dire qu'il n'y a pas de bonnes raisons de surcharge de new et delete pour ses propres classes... et vous avez touché à plusieurs légitimes de ceux qui sont ici, mais ce qui précède n'est pas l'un d'eux.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X