Remarque: Les réponses ont été données dans un ordre spécifique , mais comme de nombreux utilisateurs trient les réponses en fonction des votes, plutôt que du moment où elles ont été données, voici un index des réponses dans l'ordre dans lequel elles ont le plus de sens:
- La syntaxe générale de la surcharge d'opérateur en C ++
- Les trois règles de base de la surcharge des opérateurs en C ++
- La décision entre membre et non-membre
- Opérateurs communs à surcharger
- Opérateur d'assignation
- Opérateurs d'entrée et de sortie
- Opérateur d'appel de fonction
- Opérateurs de comparaison
- Opérateurs arithmétiques
- Indice de tableau
- Opérateurs pour les types de type pointeur
- Opérateurs de conversion
- Surcharger nouveau et supprimer
(Remarque: Ceci est censé être une entrée de la FAQ C ++ de Stack Overflow . Si vous voulez critiquer l'idée de fournir une FAQ sous cette forme, alors la publication sur la méta qui a commencé tout cela serait l'endroit pour le faire. Réponses à cette question est surveillée dans le salon de discussion C ++ , où l'idée de FAQ a commencé en premier lieu, donc votre réponse est très susceptible d'être lue par ceux qui ont eu l'idée.)
Réponses:
Opérateurs communs à surcharger
La plupart du travail des opérateurs de surcharge est le code de la chaudière. Ce n'est pas étonnant, puisque les opérateurs ne sont que du sucre syntaxique, leur travail réel pourrait être effectué par (et est souvent transmis à) des fonctions simples. Mais il est important que vous obteniez ce bon code de plaque de chaudière. Si vous échouez, le code de votre opérateur ne se compilera pas ou le code de vos utilisateurs ne se compilera pas ou le code de vos utilisateurs se comportera de manière surprenante.
Opérateur d'assignation
Il y a beaucoup à dire sur l'affectation. Cependant, la plupart d'entre eux ont déjà été mentionnés dans la célèbre FAQ de copie et d'échange de GMan , donc je vais en sauter la plupart ici, en ne listant que l'opérateur d'affectation parfait pour référence:
Opérateurs Bitshift (utilisés pour les E / S de flux)
Les opérateurs de décalage de bits
<<
et>>
, bien qu'ils soient toujours utilisés dans l'interface matérielle pour les fonctions de manipulation de bits qu'ils héritent de C, sont devenus plus courants en tant qu'opérateurs d'entrée et de sortie de flux surchargés dans la plupart des applications. Pour des informations sur la surcharge des opérateurs de manipulation de bits, consultez la section ci-dessous sur les opérateurs arithmétiques binaires. Pour implémenter votre propre format personnalisé et logique d'analyse lorsque votre objet est utilisé avec des iostreams, continuez.Les opérateurs de flux, parmi les opérateurs les plus fréquemment surchargés, sont des opérateurs d'infixes binaires pour lesquels la syntaxe ne spécifie aucune restriction quant à savoir s'ils doivent être membres ou non membres. Puisqu'ils changent leur argument gauche (ils modifient l'état du flux), ils devraient, selon les règles de base, être implémentés en tant que membres du type de leur opérande gauche. Cependant, leurs opérandes de gauche sont des flux de la bibliothèque standard, et bien que la plupart des opérateurs de sortie et d'entrée de flux définis par la bibliothèque standard soient en effet définis comme membres des classes de flux, lorsque vous implémentez des opérations de sortie et d'entrée pour vos propres types, vous ne peut pas modifier les types de flux de la bibliothèque standard. C'est pourquoi vous devez implémenter ces opérateurs pour vos propres types en tant que fonctions non membres. Les formes canoniques des deux sont les suivantes:
Lors de l'implémentation
operator>>
, la définition manuelle de l'état du flux n'est nécessaire que lorsque la lecture elle-même a réussi, mais le résultat n'est pas celui attendu.Opérateur d'appel de fonction
L'opérateur d'appel de fonction, utilisé pour créer des objets de fonction, également appelés foncteurs, doit être défini comme une fonction membre , de sorte qu'il a toujours l'
this
argument implicite des fonctions membres. En dehors de cela, il peut être surchargé pour accepter n'importe quel nombre d'arguments supplémentaires, y compris zéro.Voici un exemple de syntaxe:
Usage:
Dans toute la bibliothèque standard C ++, les objets fonction sont toujours copiés. Vos propres objets de fonction devraient donc être bon marché à copier. Si un objet fonction doit absolument utiliser des données dont la copie est coûteuse, il est préférable de stocker ces données ailleurs et de se référer à l'objet fonction.
Opérateurs de comparaison
Les opérateurs de comparaison d'infixes binaires doivent, selon les règles de base, être implémentés en tant que fonctions non membres 1 . La négation du préfixe unaire
!
doit (selon les mêmes règles) être implémentée en tant que fonction membre. (mais ce n'est généralement pas une bonne idée de le surcharger.)Les algorithmes
std::sort()
et les types standard de la bibliothèque (par exemplestd::map
) s'attendent toujoursoperator<
à être présents. Cependant, les utilisateurs de votre type s'attendront à ce que tous les autres opérateurs soient également présents , donc si vous définissezoperator<
, assurez-vous de suivre la troisième règle fondamentale de surcharge des opérateurs et définissez également tous les autres opérateurs de comparaison booléens. La manière canonique de les implémenter est la suivante:La chose importante à noter ici est que seuls deux de ces opérateurs font réellement quelque chose, les autres ne font que transmettre leurs arguments à l'un ou l'autre de ces deux pour faire le travail réel.
La syntaxe de surcharge des opérateurs booléens binaires restants (
||
,&&
) suit les règles des opérateurs de comparaison. Cependant, il est très peu probable que vous trouviez un cas d'utilisation raisonnable pour ces 2 .1 Comme pour toutes les règles empiriques, il peut parfois y avoir des raisons de rompre celle-ci également. Si c'est le cas, n'oubliez pas que l'opérande de gauche des opérateurs de comparaison binaires, qui le sera pour les fonctions membres
*this
, doit également l'êtreconst
. Ainsi, un opérateur de comparaison implémenté en tant que fonction membre devrait avoir cette signature:(Notez le
const
à la fin.)2 Il convient de noter que la version intégrée de
||
et&&
utilise la sémantique des raccourcis. Bien que ceux définis par l'utilisateur (car ils sont du sucre syntaxique pour les appels de méthode) n'utilisent pas de sémantique de raccourci. L'utilisateur s'attendra à ce que ces opérateurs aient une sémantique de raccourci, et leur code peut en dépendre, il est donc fortement conseillé de ne jamais les définir.Opérateurs arithmétiques
Opérateurs arithmétiques unaires
Les opérateurs d'incrémentation et de décrémentation unaires sont proposés à la fois en préfixe et en postfixe. Pour se différencier, les variantes postfixes prennent un argument int factice supplémentaire. Si vous surchargez l'incrémentation ou la décrémentation, assurez-vous de toujours implémenter les versions préfixée et postfixée. Voici l'implémentation canonique de l'incrémentation, la décrémentation suit les mêmes règles:
Notez que la variante postfix est implémentée en termes de préfixe. Notez également que postfix fait une copie supplémentaire. 2
La surcharge unaire moins et plus n'est pas très courante et probablement mieux évitée. Si nécessaire, ils devraient probablement être surchargés en tant que fonctions membres.
2 Notez également que la variante postfix fait plus de travail et est donc moins efficace à utiliser que la variante préfixe. C'est une bonne raison de préférer généralement l'incrément de préfixe à l'incrément de postfix. Bien que les compilateurs puissent généralement optimiser le travail supplémentaire d'incrémentation postfixe pour les types intégrés, ils pourraient ne pas être en mesure de faire de même pour les types définis par l'utilisateur (ce qui pourrait être quelque chose d'aussi innocent qu'un itérateur de liste). Une fois que vous vous êtes habitué à le faire
i++
, il devient très difficile de se rappeler de le faire à la++i
place lorsqu'ili
n'est pas de type intégré (en plus, vous devrez changer le code lors du changement de type), il est donc préférable de prendre l'habitude de toujours en utilisant l'incrément de préfixe, sauf si le suffixe est explicitement nécessaire.Opérateurs arithmétiques binaires
Pour les opérateurs arithmétiques binaires, n'oubliez pas d'obéir à la troisième surcharge de base de l'opérateur de règle: si vous fournissez
+
, fournissez également+=
, si vous fournissez-
, n'omettez pas-=
, etc. Andrew Koenig aurait été le premier à observer que l'affectation composée les opérateurs peuvent être utilisés comme base pour leurs homologues non composés. Autrement dit, l'opérateur+
est mis en œuvre en termes de+=
,-
est mis en œuvre en termes de-=
etc.Selon nos règles empiriques,
+
et ses compagnons doivent être non-membres, tandis que leurs homologues d'affectation composée (+=
etc.), en changeant leur argument de gauche, doivent être membres. Voici l'exemple de code pour+=
et+
; les autres opérateurs arithmétiques binaires doivent être implémentés de la même manière:operator+=
renvoie son résultat par référence, tandis queoperator+
renvoie une copie de son résultat. Bien sûr, le renvoi d'une référence est généralement plus efficace que le renvoi d'une copie, mais dans le cas deoperator+
, il n'y a aucun moyen de contourner la copie. Lorsque vous écriveza + b
, vous vous attendez à ce que le résultat soit une nouvelle valeur, c'est pourquoioperator+
doit renvoyer une nouvelle valeur. 3 Notez également queoperator+
prend son opérande gauche par copie plutôt que par référence const. La raison en est la même que la raison invoquée pouroperator=
prendre son argument par copie.Les opérateurs de manipulation de bits
~
&
|
^
<<
>>
doivent être implémentés de la même manière que les opérateurs arithmétiques. Cependant (à l'exception de la surcharge<<
et>>
de la sortie et de l'entrée), il existe très peu de cas d'utilisation raisonnables pour les surcharger.3 Encore une fois, la leçon à tirer de cela est
a += b
, en général, plus efficace quea + b
et devrait être préférée si possible.Indice de tableau
L'opérateur d'indice de tableau est un opérateur binaire qui doit être implémenté en tant que membre de classe. Il est utilisé pour les types de type conteneur qui permettent d'accéder à leurs éléments de données par une clé. La forme canonique de fournir ces informations est la suivante:
À moins que vous ne vouliez pas que les utilisateurs de votre classe puissent modifier les éléments de données renvoyés par
operator[]
(auquel cas vous pouvez omettre la variante non const), vous devez toujours fournir les deux variantes de l'opérateur.Si value_type est connu pour faire référence à un type intégré, la variante const de l'opérateur devrait mieux renvoyer une copie plutôt qu'une référence const:
Opérateurs pour les types de type pointeur
Pour définir vos propres itérateurs ou pointeurs intelligents, vous devez surcharger l'opérateur de déréférence de préfixe unaire
*
et l'opérateur d'accès au membre du pointeur d'infixe binaire->
:Notez que ceux-ci, aussi, auront presque toujours besoin à la fois d'une version const et d'une version non const. Pour l'
->
opérateur, sivalue_type
est de typeclass
(oustruct
ouunion
), un autreoperator->()
est appelé récursivement, jusqu'à ce que anoperator->()
renvoie une valeur de type non-classe.L'opérateur d'adresse unaire ne doit jamais être surchargé.
Pour
operator->*()
voir cette question . Il est rarement utilisé et donc rarement surchargé. En fait, même les itérateurs ne le surchargent pas.Continuez vers les opérateurs de conversion
la source
operator->()
est en fait extrêmement bizarre. Il n'est pas nécessaire de renvoyer unvalue_type*
- en fait, il peut renvoyer un autre type de classe, à condition que ce type de classe ait unoperator->()
, qui sera ensuite appelé par la suite. Cet appel récursif deoperator->()
s se poursuit jusqu'à ce qu'unvalue_type*
type de retour se produise. La démence! :)*
en termes de*=
mais ce serait gênant car l'une des premières opérations de*=
serait de créer un nouvel objet, résultat du calcul. Ensuite, après la boucle for-ijk, nous échangerions cet objet temporaire avec*this
. c'est à dire. 1. copie, 2. opérateur *, 3. échangeT* const
retour à unconst T&
déréférencement, ce qui n'est pas le cas. Ou en d'autres termes: un pointeur const n'implique pas une pointe const. En fait, il n'est pas trivial d'imiterT const *
- ce qui est la raison de tout leconst_iterator
contenu de la bibliothèque standard. Conclusion: la signature devrait êtrereference_type operator*() const; pointer_type operator->() const
L <= R
peut également être exprimé comme!(R < L)
au lieu de!(L > R)
. Pourrait économiser une couche supplémentaire d'inlining dans les expressions difficiles à optimiser (et c'est aussi la façon dont Boost.Operators l'implémente).Les trois règles de base de la surcharge des opérateurs en C ++
En ce qui concerne la surcharge des opérateurs en C ++, vous devez suivre trois règles de base . Comme pour toutes ces règles, il existe en effet des exceptions. Parfois, les gens s'en sont écartés et le résultat n'était pas un mauvais code, mais ces écarts positifs sont rares. À tout le moins, 99 des 100 écarts de ce type que j'ai vus étaient injustifiés. Cependant, il pourrait tout aussi bien être de 999 sur 1000. Donc, vous feriez mieux de vous en tenir aux règles suivantes.
Chaque fois que le sens d'un opérateur n'est pas clairement clair et incontesté, il ne doit pas être surchargé. Au lieu de cela, fournissez une fonction avec un nom bien choisi.
Fondamentalement, la règle avant tout pour surcharger les opérateurs, en son cœur, dit: Ne le faites pas . Cela peut sembler étrange, car il y a beaucoup à savoir sur la surcharge des opérateurs et donc beaucoup d'articles, de chapitres de livres et d'autres textes traitent de tout cela. Mais malgré ces preuves apparemment évidentes, il n'y a que très peu de cas où la surcharge de l'opérateur est appropriée. La raison en est qu'en réalité il est difficile de comprendre la sémantique derrière l'application d'un opérateur à moins que l'utilisation de l'opérateur dans le domaine d'application soit bien connue et incontestée. Contrairement à la croyance populaire, ce n'est presque jamais le cas.
Respectez toujours la sémantique bien connue de l'opérateur.
C ++ ne pose aucune limitation sur la sémantique des opérateurs surchargés. Votre compilateur acceptera volontiers le code qui implémente l'
+
opérateurbinaireà soustraire de son opérande droit. Cependant, les utilisateurs d'un tel opérateur nedouterait jamais l'expressiona + b
de soustrairea
deb
. Bien sûr, cela suppose que la sémantique de l'opérateur dans le domaine d'application est incontestée.Fournissez toujours tout sur un ensemble d'opérations connexes.
Les opérateurs sont liés les uns aux autres et aux autres opérations. Si votre type le prend en charge
a + b
, les utilisateurs s'attendront également à pouvoir appelera += b
. S'il prend en charge l'incrément de préfixe++a
, ils s'attendronta++
à fonctionner également. S'ils peuvent vérifier sia < b
, ils s'attendent très certainement à pouvoir également vérifier sia > b
. S'ils peuvent copier-construire votre type, ils s'attendent à ce que l'affectation fonctionne également.Passez à La décision entre membre et non-membre .
la source
boost::spirit
lol.+
pour la concaténation de chaînes est une violation, mais c'est maintenant devenu une pratique bien établie, de sorte que cela semble naturel. Bien que je me souvienne d'une classe de cordes de brassage maison que j'ai vue dans les années 90 qui utilisait le binaire&
à cette fin (se référant à BASIC pour la praxis établie). Mais, oui, le mettre dans la librairie std a fondamentalement mis cela dans la pierre. Il en va de même pour les abus<<
et>>
pour IO, BTW. Pourquoi le décalage à gauche serait-il l'opération de sortie évidente? Parce que nous l'avons tous appris quand nous avons vu notre premier "Bonjour, monde!" application. Et pour aucune autre raison.operator==
est qu'il devrait s'agir d'une relation d'équivalence (IOW, vous ne devriez pas utiliser de NaN non signalant). Il existe de nombreuses relations d'équivalence utiles sur les conteneurs. Que signifie l'égalité? "a
égalb
" signifie celaa
etb
a la même valeur mathématique. Le concept de valeur mathématique d'un (non-NaN)float
est clair, mais la valeur mathématique d'un conteneur peut avoir de nombreuses définitions utiles distinctes (de type récursif). La définition la plus forte de l'égalité est "ce sont les mêmes objets", et c'est inutile.La syntaxe générale de la surcharge d'opérateur en C ++
Vous ne pouvez pas changer la signification des opérateurs pour les types intégrés en C ++, les opérateurs ne peuvent être surchargés que pour les types définis par l'utilisateur 1 . Autrement dit, au moins l'un des opérandes doit être d'un type défini par l'utilisateur. Comme pour les autres fonctions surchargées, les opérateurs ne peuvent être surchargés qu'une seule fois pour un certain ensemble de paramètres.
Tous les opérateurs ne peuvent pas être surchargés en C ++. Parmi les opérateurs qui ne peuvent pas être surchargés figurent:
.
::
sizeof
typeid
.*
et le seul opérateur ternaire en C ++,?:
Les opérateurs qui peuvent être surchargés en C ++ sont les suivants:
+
-
*
/
%
et+=
-=
*=
/=
%=
(tous les infixes binaires);+
-
(préfixe unaire);++
--
(préfixe unaire et suffixe)&
|
^
<<
>>
et&=
|=
^=
<<=
>>=
(tous les infixes binaires);~
(préfixe unaire)==
!=
<
>
<=
>=
||
&&
(tous les infixes binaires);!
(préfixe unaire)new
new[]
delete
delete[]
=
[]
->
->*
,
(tous les infixes binaires);*
&
(tout préfixe unaire)()
(appel de fonction, infixe n-aire)Cependant, le fait que vous pouvez surcharger tout cela ne signifie pas que vous devriez le faire. Voir les règles de base de la surcharge de l'opérateur.
En C ++, les opérateurs sont surchargés sous forme de fonctions avec des noms spéciaux . Comme pour les autres fonctions, les opérateurs surchargés peuvent généralement être implémentés soit en tant que fonction membre du type de leur opérande gauche, soit en tant que fonctions non membres . Que vous soyez libre de choisir ou obligé d'utiliser l'un ou l'autre dépend de plusieurs critères. 2 Un opérateur unaire
@
3 , appliqué à un objet x, est appelé soit comme,operator@(x)
soit commex.operator@()
. Un opérateur d'infixe binaire@
, appliqué aux objetsx
ety
, est appelé soit comme,operator@(x,y)
soit commex.operator@(y)
. 4Les opérateurs implémentés en tant que fonctions non membres sont parfois amis du type de leur opérande.
1 Le terme «défini par l'utilisateur» peut être légèrement trompeur. C ++ fait la distinction entre les types intégrés et les types définis par l'utilisateur. Aux premiers appartiennent par exemple int, char et double; à ces derniers appartiennent tous les types struct, class, union et enum, y compris ceux de la bibliothèque standard, même s'ils ne sont pas, en tant que tels, définis par les utilisateurs.
2 Ceci est couvert dans une partie ultérieure de cette FAQ.
3 Le
@
n'est pas un opérateur valide en C ++, c'est pourquoi je l'utilise comme espace réservé.4 Le seul opérateur ternaire en C ++ ne peut pas être surchargé et le seul opérateur n-aire doit toujours être implémenté en tant que fonction membre.
Passez aux Trois règles de base de la surcharge d'opérateur en C ++ .
la source
~
est un préfixe unaire, pas un infixe binaire..*
est absent de la liste des opérateurs non surchargeables.:)
operator+()
tant que fonction membre, mais lui a donné la signature d'une fonction libre. Voyez ici .La décision entre membre et non-membre
Les opérateurs binaires
=
(affectation),[]
(abonnement à un tableau),->
(accès membre), ainsi que l'()
opérateur n-aire (appel de fonction), doivent toujours être implémentés en tant que fonctions membres , car la syntaxe du langage le requiert.D'autres opérateurs peuvent être implémentés en tant que membres ou non-membres. Cependant, certaines d'entre elles doivent généralement être implémentées en tant que fonctions non membres, car leur opérande gauche ne peut pas être modifié par vous. Les plus importants d'entre eux sont les opérateurs d'entrée et de sortie
<<
et>>
, dont les opérandes de gauche sont des classes de flux de la bibliothèque standard que vous ne pouvez pas modifier.Pour tous les opérateurs où vous devez choisir de les implémenter en tant que fonction membre ou fonction non membre, utilisez les règles générales suivantes pour décider:
Bien sûr, comme pour toutes les règles de base, il existe des exceptions. Si vous avez un type
et vous souhaitez surcharger les opérateurs d'incrémentation et de décrémentation pour cela, vous ne pouvez pas le faire en tant que fonctions membres, car en C ++, les types enum ne peuvent pas avoir de fonctions membres. Vous devez donc le surcharger comme une fonction gratuite. Et
operator<()
pour un modèle de classe imbriqué dans un modèle de classe, il est beaucoup plus facile d'écrire et de lire lorsqu'il est fait en tant que fonction membre en ligne dans la définition de classe. Mais ce sont en effet de rares exceptions.(Cependant, si vous faites une exception, n'oubliez pas le problème de
const
-ness pour l'opérande qui, pour les fonctions membres, devient l'this
argument implicite . Si l'opérateur en tant que fonction non membre prend son argument le plus à gauche commeconst
référence , le même opérateur qu'une fonction membre doit avoir unconst
à la fin pour faire*this
uneconst
référence.)Passez à Opérateurs communs pour surcharger .
la source
operator+=()
ne pas être membre. Il doit changer son opérande gauche, donc par définition, il doit creuser profondément dans ses entrailles. Que gagneriez-vous à ne pas en faire membre?operator +=
lesappend
méthodes et. Laappend
méthode est plus complète, car vous pouvez ajouter une sous-chaîne du paramètre de l'index i à l'index n -1:append(string, start, end)
Il semble logique que l'+=
appel ajoute avecstart = 0
etend = string.size
. À ce moment, append pourrait être une méthode membre, maisoperator +=
n'a pas besoin d'être membre, et en faire un non-membre diminuerait la quantité de code jouant avec les entrailles String, donc c'est une bonne chose .... ^ _ ^ ...Opérateurs de conversion (également appelés conversions définies par l'utilisateur)
En C ++, vous pouvez créer des opérateurs de conversion, des opérateurs qui permettent au compilateur de convertir entre vos types et d'autres types définis. Il existe deux types d'opérateurs de conversion, implicites et explicites.
Opérateurs de conversion implicites (C ++ 98 / C ++ 03 et C ++ 11)
Un opérateur de conversion implicite permet au compilateur de convertir implicitement (comme la conversion entre
int
etlong
) la valeur d'un type défini par l'utilisateur en un autre type.Voici une classe simple avec un opérateur de conversion implicite:
Les opérateurs de conversion implicites, comme les constructeurs à un argument, sont des conversions définies par l'utilisateur. Les compilateurs accordent une conversion définie par l'utilisateur lorsqu'ils tentent de faire correspondre un appel à une fonction surchargée.
Au début, cela semble très utile, mais le problème avec cela est que la conversion implicite entre même en action lorsqu'elle n'est pas attendue. Dans le code suivant,
void f(const char*)
sera appelé carmy_string()
n'est pas une valeur l , donc la première ne correspond pas:Les débutants se trompent facilement et même les programmeurs C ++ expérimentés sont parfois surpris parce que le compilateur choisit une surcharge qu'ils ne soupçonnaient pas. Ces problèmes peuvent être atténués par des opérateurs de conversion explicites.
Opérateurs de conversion explicite (C ++ 11)
Contrairement aux opérateurs de conversion implicites, les opérateurs de conversion explicites n'interviendront jamais lorsque vous ne vous y attendez pas. Voici une classe simple avec un opérateur de conversion explicite:
Remarquez le
explicit
. Maintenant, lorsque vous essayez d'exécuter le code inattendu à partir des opérateurs de conversion implicites, vous obtenez une erreur de compilation:Pour appeler l'opérateur de transtypage explicite, vous devez utiliser
static_cast
un transtypage de style C ou un transtypage de style constructeur (c'est-à-direT(value)
).Cependant, il existe une exception: le compilateur est autorisé à effectuer une conversion implicite vers
bool
. De plus, le compilateur n'est pas autorisé à effectuer une autre conversion implicite après sa conversion enbool
(un compilateur est autorisé à effectuer 2 conversions implicites à la fois, mais seulement 1 conversion définie par l'utilisateur au maximum).Étant donné que le compilateur ne transtypera pas "passé"
bool
, les opérateurs de conversion explicites suppriment désormais le besoin de l' idiome Safe Bool . Par exemple, les pointeurs intelligents avant C ++ 11 utilisaient l'idiome Safe Bool pour empêcher les conversions en types intégraux. En C ++ 11, les pointeurs intelligents utilisent à la place un opérateur explicite car le compilateur n'est pas autorisé à convertir implicitement en un type intégral après avoir converti explicitement un type en bool.Continuez vers Surcharge
new
etdelete
.la source
Surcharge
new
etdelete
Remarque: Cela ne concerne que la syntaxe de la surcharge
new
etdelete
non la mise en œuvre de tels opérateurs surchargés. Je pense que la sémantique de la surchargenew
etdelete
mérite leur propre FAQ , dans le sujet de la surcharge de l'opérateur, je ne peux jamais lui rendre justice.Les bases
En C ++, lorsque vous écrivez une nouvelle expression comme
new T(arg)
deux choses se produisent lorsque cette expression est évaluée: Tout d' abordoperator new
est invoquée pour obtenir la mémoire brute, puis le constructeur approprié deT
est invoqué pour transformer cette mémoire brute en un objet valide. De même, lorsque vous supprimez un objet, son destructeur est d'abord appelé, puis la mémoire est renvoyéeoperator delete
.C ++ vous permet de régler ces deux opérations: la gestion de la mémoire et la construction / destruction de l'objet sur la mémoire allouée. Ce dernier se fait en écrivant des constructeurs et des destructeurs pour une classe. La gestion précise de la mémoire se fait en écrivant les vôtres
operator new
etoperator delete
.La première des règles de base de la surcharge de l'opérateur - ne le faites pas - s'applique particulièrement à la surcharge
new
et àdelete
. Presque les seules raisons de surcharger ces opérateurs sont les problèmes de performances et les contraintes de mémoire , et dans de nombreux cas, d'autres actions, telles que les modifications des algorithmes utilisés, fourniront un rapport coût / gain beaucoup plus élevé que d'essayer de modifier la gestion de la mémoire.La bibliothèque standard C de est livré avec un ensemble de prédéfinis
new
et lesdelete
opérateurs. Les plus importants sont les suivants:Les deux premiers allouent / désallouent de la mémoire pour un objet, les deux derniers pour un tableau d'objets. Si vous fournissez vos propres versions de celles-ci, elles ne surchargeront pas, mais remplaceront celles de la bibliothèque standard.
Si vous surchargez
operator new
, vous devez toujours surcharger également la correspondanceoperator delete
, même si vous n'avez jamais l'intention de l'appeler. La raison en est que, si un constructeur lance lors de l'évaluation d'une nouvelle expression, le système d'exécution retournera la mémoire à laoperator delete
correspondanceoperator new
qui a été appelée pour allouer la mémoire pour créer l'objet. Si vous ne fournissez pas de correspondanceoperator delete
, celui par défaut est appelé, ce qui est presque toujours faux.Si vous surchargez
new
etdelete
, vous devez également envisager de surcharger les variantes de tableau.Placement
new
C ++ permet aux nouveaux opérateurs et aux opérateurs de suppression de prendre des arguments supplémentaires.
Le nouveau placement dit vous permet de créer un objet à une certaine adresse qui est transmise à:
La bibliothèque standard est livrée avec les surcharges appropriées des opérateurs new et delete pour cela:
Notez que, dans l'exemple de code pour le placement nouveau donné ci-dessus,
operator delete
n'est jamais appelé, sauf si le constructeur de X lève une exception.Vous pouvez également surcharger
new
etdelete
avec d'autres arguments. Comme pour l'argument supplémentaire pour placement new, ces arguments sont également répertoriés entre parenthèses après le mot clénew
. Simplement pour des raisons historiques, de telles variantes sont souvent également appelées placement nouveau, même si leurs arguments ne sont pas pour placer un objet à une adresse spécifique.Nouveau et suppression spécifiques à la classe
Le plus souvent, vous souhaiterez affiner la gestion de la mémoire car la mesure a montré que les instances d'une classe spécifique ou d'un groupe de classes associées sont souvent créées et détruites et que la gestion de la mémoire par défaut du système d'exécution, réglée pour performances générales, traite inefficacement dans ce cas spécifique. Pour améliorer cela, vous pouvez surcharger new et delete pour une classe spécifique:
Surchargé ainsi, new et delete se comportent comme des fonctions membres statiques. Pour les objets de
my_class
, l'std::size_t
argument sera toujourssizeof(my_class)
. Cependant, ces opérateurs sont également appelés pour les objets alloués dynamiquement des classes dérivées , auquel cas il pourrait être supérieur à cela.Nouveau global et supprimer
Pour surcharger le nouveau global et le supprimer, remplacez simplement les opérateurs prédéfinis de la bibliothèque standard par les nôtres. Cependant, cela doit rarement être fait.
la source
nothrow
nouvelles.Pourquoi la
operator<<
fonction de streaming d'objets versstd::cout
ou vers un fichier ne peut-elle pas être une fonction membre?Disons que vous avez:
Cela dit, vous ne pouvez pas utiliser:
Puisque
operator<<
est surchargé en tant que fonction membre deFoo
, le LHS de l'opérateur doit être unFoo
objet. Cela signifie que vous devrez utiliser:ce qui est très peu intuitif.
Si vous le définissez comme une fonction non membre,
Vous pourrez utiliser:
ce qui est très intuitif.
la source