Comment dois-je écrire ISO C++ Standard conforme personnalisé nouveau et supprimer des opérateurs?
Comment dois-je écrire ISO C++ standard conforme personnalisé new
et delete
opérateurs?
C'est dans le prolongement de La surcharge new et delete très éclairant C++ FAQ, La surcharge d'opérateur, et son suivi, Pourquoi devrait-on remplacer nouveau par défaut et supprimer des opérateurs?
Section 1: la Rédaction d'une norme-conforme new
opérateur
- Partie 1: Comprendre les exigences pour la rédaction d'un custom
new
de l'opérateur - Partie 2: Comprendre le
new_handler
exigences - Partie 3: Comprendre le scénario exigences
Section 2: la Rédaction d'une norme-conforme delete
opérateur
(Note: Ceci est destiné à être une entrée à Un Débordement de pile du C++ FAQ. Si vous voulez une critique de l'idée de fournir une FAQ dans le présent formulaire, puis l'affichage sur des méta qui a commencé toute cette serait l'endroit pour le faire. Les réponses à cette question sont surveillés dans le C++ tchat, 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.)
Remarque: La réponse est basée sur les enseignements de Scott Meyers Plus Efficace C++ et le Standard ISO C++.
- wow, les gens sont leurs downvotes au début! - Je devine que vous n'avez pas encore fini de poser votre question encore? Je pense que c'est un bon endroit pour discuter de ces problèmes, +1 de moi.
- L'OP a fini, comme il est maintenant occupé à écrire une longue réponse. Le petit texte états ", basé sur l'apprentissage de Scott Meyer Plus Effective C++" (notez les deux orthographe, fautes de frappe, y compris le nom de Scott Meyers).
- Regarde comme il y a des gens qui ne vous aiment pas beaucoup 🙂 personnellement, je n'aime pas les réponses décousues comme celui-ci, je sens qu'il appartient dans une section FAQ quelque part au lieu d'être perdu parmi les milliers de questions qui ont été postés à chaque jour. Mais +1 pour l'effort.
- C'est une suite entrée de la FAQ. Nous avons une excellente Surcharge d'Opérateur FAQ, mais ce sujet est tellement vaste qu'il faudrait une entrée spéciale dans lui-même.
- Je ne voulais pas dire la FAQ balise, mais une section dédiée du site web, un peu comme le Parashift C++ FAQ. Mais puisque nous n'avons pas cela, c'est la meilleure chose suivante.
- il y a un peu d'un article dédié: c++-faq, qui est lié à partir de la le c++ balise de la page.
- Cette question est en fait fréquemment posées?
- Je pense que la "foire aux Questions" pourrait ainsi inclure "les Réponses Qui Sont Plus Utile De Savoir Que Vous avez Jamais Réalisé, Quand Vous Avez fait un Travail Connexe Fréquemment"
- McNellis: Nous avons une excellente FAQ du Sbi, sur la surcharge d'Opérateur & il a omis cet aspect pour la raison qu'il a besoin d'une mention spéciale. Cette tente de faire de cette FAQ complète.
- Mais cette question est fréquemment posée? Si pas, alors que je n'ai pas d'objection à la question posée et la réponse ici, il ne devrait pas avoir le [c++-faq] balise. L'étiquette est déjà trop bruyant.
- En fait, je serais d'accord avec ça.
c++-faq
est pas pour tous les auto-répondu livre de style Q&A qu'un utilisateur normal ne peut penser. - Bel article, mais s'il vous plaît vous détendre sur la typographie! Un niveau de la décoration à la fois-et pouvez-vous faire avec de moins en moins de backticks? Le tout est un peu dur sur les yeux. Je suis heureux de le modifier un peu si vous le souhaitez.
- Pourquoi ne pas suivre le lien à la question sur les meta qui a commencé tout cela, de lire à travers les discussions, et de proposer une telle section AFIN d'y poser une question sur meta? Ou à la baisse sur le chat et de discuter de cela quelques jours. Si vous lisez à travers les discussions sur meta, vous verrez que, quand nous avons commencé, nous ne savons pas quelle forme cette FAQ chose doit prendre, et le détournement de la
c++-faq
balise n'était que le premier-le meilleur de la béquille qui l'accompagnent, destiné à être une solution temporaire jusqu'à ce que nous avons trouvé mieux. Je serais très heureux si quelques nouvelles idées sur la façon de le faire venir et pourrait être discutée. - SB: sentez-vous Svp libre pour, en Fait, c'est dans l'un de nos discussions que j'ai réalisé l'existant, la surcharge d'opérateur FAQ n'a pas le couvrir.
- Est-il une raison pour que tu ne pouvais pas juste faire ce qu'une seule réponse au lieu de 5? Aussi, pourquoi n'est-il pas Wiki de la communauté?
- Bolas: AFIN de limiter le nombre de caractères dans une réponse, et de ce fait ils ne pouvaient pas juste tenir dans une réponse, Donc distincte de réponses. Ce qui était censé être une entrée à la C++-Faq pour terminer le Surcharge des opérateurs Faq qui est déjà dans l'existence, Ce n'est pas un wiki de la communauté parce que c'était juste une seule personne(moi) qui a formulé la réponse de la Faq, Pour être franc, la plupart des utilisateurs downvoted sans même botherig de le lire. Étant donné que ce a reçu 10 Downvotes(pas de raisons?), J'rassembler la communauté ne l'aime pas, & dans un sens, je regrette de mettre dans les efforts à venir avec ce.
- Le fin de la communauté wiki est plus que juste qui écrit la réponse. C'est aussi plus de personnes sont en mesure de corriger les erreurs, de celui qui est en haut/en bas rep voix (c'est à dire: personne n'), etc.
- Bolas: Point, personne n'est la lecture de ce. Laissons de côté l'évaluation de ce qui est mauvais, ou les corriger en elle.Et les entrées ajoutées en tant que contribue n'ont jamais été hostorically marqué Wiki de la Communauté. Vous pouvez le déposer dans le C++ Loung salle de parfois pour discuter de la philosophie derrière le C++ FAQ et les gens qui sont venus avec l'idée de se lancer dans la première place.
- Utilisé pour être la raison de l'introduction de la CW, cependant, il n'est plus utile pour qui, parce que tout le monde peut intervenir et de modifier, même ceux avec très faible rep. OMI, la seule utilisation de la CW aujourd'hui est pour le système d'appuyer sur l'interrupteur de questions que de prendre trop d'attention (défini à peu près comme "plus d'une douzaine de réponses"), de sorte que les cas les plus graves du système de vote de la faiblesse inhérente (le droit de vote est remis par popularité, plutôt que de justesse) ne pas causer comme beaucoup des ravages.
- Un commentaire sur le nouvel opérateur contre l'opérateur new[] et la nothrow-nouvel opérateur serait également utile.
Vous devez vous connecter pour publier un commentaire.
Partie I
Cette entrée de la FAQ C++ expliqué pourquoi on peut avoir envie de surcharge
new
etdelete
opérateurs pour sa propre classe. Cette FAQ tente d'expliquer comment on le fait dans un standard conforme façon.La mise en œuvre d'une coutume
new
opérateurLa norme C++ (§18.4.1.1) définit
operator new
comme:Le C++ standard spécifie la sémantique que des versions personnalisées de ces opérateurs ont à obéir au §3.7.3 et §18.4.1
Résumons les exigences.
Exigence n ° 1: Il devrait allouer dynamiquement au moins
size
octets de mémoire et retourne un pointeur vers la mémoire allouée. Citation de la norme C++, section 3.7.4.1.3:La norme impose en outre:
Cela nous donne en outre à des exigences importantes:
Exigence n ° 2: La fonction d'allocation mémoire nous utilisons (généralement
malloc()
ou certains autres allocateur personnalisé) doit retourner une correctement alignés pointeur vers la mémoire allouée, qui peut être converti en un pointeur d'un type d'objet et utilisé pour accéder à l'objet.Exigence n ° 3: Notre propre opérateur
new
doit retourner une légitime pointeur de même lors de zéro octets sont demandés.L'un de l'évidente exigences qui peuvent même être déduite à partir de
new
prototype est:Exigence n ° 4: Si
new
ne peut pas allouer de la mémoire dynamique de la taille demandée, alors il doit lever une exception de typestd::bad_alloc
.Mais! Il n'y a plus que ce que rencontre l'oeil: Si vous regardez de plus près à la
new
opérateur la documentation (citation de la norme suit plus bas), il indique:De comprendre comment notre coutume
new
besoin à l'appui de cette exigence, il faut comprendre:Quel est le
new_handler
etset_new_handler
?new_handler
est une définition de type d'un pointeur vers une fonction qui prend et ne renvoie rien, etset_new_handler
est une fonction qui prend et retourne unnew_handler
.set_new_handler
's paramètre est un pointeur vers la fonction de nouvel opérateur devrait appeler si il ne peut pas allouer de la mémoire demandée. Sa valeur de retour est un pointeur précédemment enregistré fonction de gestionnaire, ou null si il n'y avait pas de précédent gestionnaire.Un moment opportun pour un exemple de code pour mettre les choses au clair:
Dans l'exemple ci-dessus,
operator new
(le plus probable) sera impossible d'allouer de l'espace pour 100 000 000 d'entiers, et la fonctionoutOfMemHandler()
sera appelée, et le programme s'arrêtera après l'émission d'un message d'erreur.Il est important de noter ici que lorsque
operator new
est incapable de remplir une demande de mémoire, il appelle lanew-handler
de fonction à plusieurs reprises jusqu'à ce qu'il peut trouver assez de mémoire ou il n'y a plus de nouveaux gestionnaires. Dans l'exemple ci-dessus, à moins que nous appelonsstd::abort()
,outOfMemHandler()
serait appelé à plusieurs reprises. Par conséquent, le gestionnaire doit s'assurer que la prochaine allocation réussit, ou inscrire un autre gestionnaire, ou n'enregistrent pas de gestionnaire, ou pas de retour (c'est à dire la fin du programme). Si il n'y a pas de nouvelles de gestionnaire et de l'allocation échoue, l'opérateur va lancer une exception.Suite 1
std::set_new_handler
. Alors ma version de nouveau gestionnaire d'appel de l'ancienne versionif my version failed to provide any emergency space
. De cette façon, si une autre bibliothèque a installé un nouveau gestionnaire qui sera appelée comme prévu par la bibliothèque.new
est dansnamespace std
?std::bad_alloc
à la place? Parce que si vous ne std::abort (), cela n'a pas cette chance.Partie II
... suite
Étant donné le comportement de
operator new
de l'exemple, un bien conçunew_handler
doit effectuez l'une des opérations suivantes:Faire plus de mémoire disponible: Cela peut permet la prochaine allocation de mémoire tentative à l'intérieur de nouvel opérateur de boucle pour réussir. Une façon de mettre en œuvre ce qui est d'allouer un bloc de mémoire au démarrage du programme, puis relâchez-la pour l'utiliser dans le programme la première fois le nouveau gestionnaire est appelé.
Installer un autre nouveau-gestionnaire: Si le nouveau gestionnaire peut pas faire plus de mémoire disponible, et de là est un autre nouveau-gestionnaire peut, alors le courant de la nouvelle-gestionnaire peut installer les autres nouveau-chien à sa place (en appelant
set_new_handler
). La prochaine fois opérateur de nouveaux appels à la nouvelle fonction de gestionnaire, elle permettra d'obtenir la plus récente.(Une variation sur ce thème est pour un nouveau gestionnaire pour modifier son propre comportement, de sorte que la prochaine fois qu'il est invoqué, il fait quelque chose de différent. Une façon d'y parvenir est d'avoir la nouvelle-gestionnaire de modifier statique, de l'espace de noms spécifique, ou de données mondiale qui affecte le nouveau gestionnaire du comportement.)
Désinstaller le nouveau gestionnaire: Cela se fait en passant un pointeur null pour
set_new_handler
. En l'absence de nouveau gestionnaire d'installé,operator new
va lever une exception (convertibles)std::bad_alloc
) lorsque l'allocation de mémoire est un échec.Lever une exception convertibles
std::bad_alloc
. Ces exceptions ne sont pas être pris paroperator new
, mais va se propager vers le site d'origine de la demande de mémoire.Pas de retour: En appelant
abort
ouexit
.De mettre en œuvre une classe spécifique
new_handler
nous avons de fournir une classe avec ses propres versions deset_new_handler
etoperator new
. La classeset_new_handler
permet aux clients de spécifier la nouvelle-gestionnaire pour la classe (exactement comme la norme deset_new_handler
permet aux clients de spécifier la nouvelle-gestionnaire). La classeoperator new
s'assure que les classe spécifique de la nouvelle-gestionnaire est utilisé à la place de la global new-gestionnaire de la mémoire pour les objets de classe est attribué.Maintenant que nous comprenons
new_handler
&set_new_handler
mieux, nous sommes en mesure de modifier la Exigence n ° 4 convenablement comme:Exigence N ° 4 (Amélioré):
Notre
operator new
devriez essayer d'allouer de la mémoire plus d'une fois, l'appel de la nouvelle fonction de traitement après chaque échec. L'hypothèse ici est que la nouvelle fonction de gestion pourraient être en mesure de faire quelque chose pour libérer de la mémoire. Seulement lorsque le pointeur de la nouvelle fonction de gestion estnull
neoperator new
lever une exception.Comme promis, la référence de la Norme:
Section 3.7.4.1.3:
Armé avec la #4 exigences, nous allons tenter le pseudo-code pour notre
new operator
:Poursuite 2
Partie III
... suite
Noter que nous ne pouvons pas obtenir le nouveau gestionnaire de pointeur de fonction directement, nous devons appeler
set_new_handler
pour savoir ce que c'est. C'est rudimentaire mais efficace, au moins pour single-threaded code. Dans un environnement multithread, probablement une sorte de verrou à manipuler le (global) des structures de données sous-tend la nouvelle fonction de traitement sera nécessaire. (Plus citation/détails sont les bienvenus sur ce.)Aussi, nous avons une boucle infinie et le seul moyen de sortir de la boucle est de la mémoire pour être correctement alloué, ou pour la nouvelle fonction de gestion des pour en faire l'une des choses que nous avons déduit avant. À moins que le
new_handler
ne une de ces choses, cette boucle à l'intérieur denew
opérateur aura jamais de fin.Une mise en garde: Noter que la norme (
§3.7.4.1.3
, cité ci-dessus) ne disent pas explicitement que la surchargenew
opérateur doit mettre en œuvre une boucle infinie, mais il ne fait que dire que c'est le comportement par défaut.Donc ce détail est ouvert à l'interprétation, mais la plupart des compilateurs (GCC et Microsoft Visual C++) faire mettre en œuvre cette fonctionnalité boucle (vous pouvez compiler les exemples de code fournis plus haut).en outre, depuis une C++ authory comme Scott Meyers suggère cette approche, il est assez raisonnable.Scénarios spéciaux
Considérons le scénario suivant.
Comme cette FAQ, explique, une raison commune pour l'écriture d'un personnalisé du gestionnaire de mémoire est d'optimiser l'allocation pour les objets d'une classe spécifique, pas pour une classe ou d'une de
ses classes dérivées, ce qui signifie essentiellement que notre nouvel opérateur pour la classe de Base est généralement à l'écoute pour des objets de taille
sizeof(Base)
-rien de plus et rien de plus petites.Dans l'exemple ci-dessus, en raison de l'héritage de la classe dérivée
Derived
hérite le nouvel exploitant de la classe de Base. Cela rend l'opérateur appelant de nouveau dans une classe de base pour allouer de la mémoire pour un objet d'une classe dérivée possible. La meilleure façon pour notreoperator new
à gérer cette situation consiste à détourner ces appels demandant la "mauvaise" la quantité de mémoire à la norme de l'opérateur new, comme ceci:Noter que, la vérification de la taille aussi incoprporates notre exigence n ° 3. C'est parce que tous autoportant les objets ont une taille non nulle en C++, donc
sizeof(Base)
ne peut jamais être égal à zéro, donc si la taille est égale à zéro, la demande sera transmise à::operator new
, et il est gauranteed qu'il gère en standard conforme.Citation: Par le créateur de C++ lui-même, M. Bjarne Stroustrup.
La mise en œuvre d'une coutume opérateur delete
La Norme C++ (
§18.4.1.1
) bibliothèque définitoperator delete
comme:Nous répétons l'exercice de collecte des exigences pour l'écriture de notre coutume
operator delete
:Exigence n ° 1:
Il doit retourner
void
et son premier paramètre doit êtrevoid*
. Une coutumedelete operator
peut avoir plus d'un seul paramètre, mais bien nous avons juste besoin d'un paramètre à passer le pointeur pointant vers la mémoire allouée.Citation de la Norme C++:
Section §3.7.3.2.2:
"Chaque fonction de libération est de retour void et son premier paramètre est vide*. Une fonction de libération peut avoir plus d'un paramètre....."
Exigence n ° 2: Il doit garantir qu'il est plus sûr de supprimer un pointeur null dans le passé comme argument.
Citation par la Norme C++:
Section §3.7.3.2.3:
Exigence n ° 3:
Si le pointeur passé n'est pas
null
, puis ledelete operator
devrait libérer la dynamique de la mémoire allouée et attribuée au pointeur.Citation par la Norme C++:
Section §3.7.3.2.4:
Exigence n ° 4:
Aussi, depuis notre spécifiques à la classe de nouvel opérateur transfère les demandes de la "mauvaise" taille de
::operator new
, Nous DOIT de l'avant "à tort" de la taille de la suppression des demandes de::operator delete
.Donc, sur la base des exigences, nous avons résumé ci-dessus est ici d'un standard conforme pseudo-code pour un custom
delete operator
:free
et il suffit de supposer que la valeur par défautoperator new
utilisémalloc
(ou autre) ?