2283 votes

La surcharge d'opérateur

Quelles sont les règles de base et les idiomes de la surcharge d'opérateur en C++?

Remarque: Les réponses ont été données dans un ordre spécifique, mais depuis de nombreux utilisateurs de trier les réponses en fonction des votes, plutôt que le temps qu'ils ont été donnés, voici un index des réponses dans l'ordre dans lequel ils font plus de sens:

(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.)

523voto

sbi Points 100828

Les Trois Règles de Base de la Surcharge d'Opérateur en C++

Quand il s'agit de la surcharge d'opérateur en C++, il y a trois règles de base que vous devez suivre. Comme avec toutes ces règles, il y a en effet des exceptions. Parfois, les gens ont dévié de leur part, et le résultat n'était pas mauvais code, mais ces écarts sont peu et loin entre. À tout le moins, 99 sur 100 de tels écarts que j'ai vus étaient injustifiées. Cependant, il pourrait aussi bien avoir été 999 1000. Alors vous feriez mieux de suivre les règles suivantes.

  1. Chaque fois que le sens d'un opérateur n'est évidemment pas clair et incontesté, il ne devrait pas être surchargé. Au lieu de cela, fournir une fonction avec un nom choisi.
    Fondamentalement, et avant la première règle de la surcharge des opérateurs, en son coeur même, dit: ne le faites pas. Cela peut sembler étrange, car il ya beaucoup à être connu au sujet de la surcharge d'opérateur et donc un grand nombre d'articles, de chapitres de livres, et d'autres textes de traiter avec tout cela. Mais en dépit de cette apparente évidence des preuves, il n'y a que très peu de cas où la surcharge d'opérateur est approprié. La raison en est qu'en fait il est difficile de comprendre la sémantique derrière la demande de l'exploitant, à moins que l'utilisation de l'opérateur dans le domaine d'application est bien connu et incontesté. Contrairement à la croyance populaire, ce n'est presque jamais le cas.

  2. Toujours tenir à l'opérateur bien connu de la sémantique.
    C++ ne pose pas de limitations sur la sémantique des opérateurs surchargés. Votre compilateur sera heureux d'accepter le code qui implémente le binaire + opérateur de soustraire de son opérande de droite. Toutefois, les utilisateurs d'un tel opérateur n'aurait jamais soupçonner l'expression a + b soustraire a de b. Bien sûr, cela suppose que la sémantique de l'opérateur dans le domaine d'application n'est pas contesté.

  3. Toujours fournir tout un ensemble d'opérations liées.
    Les opérateurs sont liés les uns aux autres et à d'autres opérations. Si votre type prend en charge l' a + b, les utilisateurs s'attendent à être en mesure d'appeler a += b, trop. Si elle prend en charge préfixe incrément ++a, ils attendent a++ . Si ils peuvent vérifier si a < b, ils vont certainement s'attendre également à être en mesure de vérifier si a > b. Si ils peuvent copier-construire votre type, ils s'attendent à l'exécution d'un travail.


Continuer à La Décision entre membres et Non-Membres.

274voto

sbi Points 100828

La Syntaxe Générale de la surcharge d'opérateur en C++

Vous ne pouvez pas modifier le sens des opérateurs pour les types intégrés en C++, les opérateurs ne peuvent être surchargé pour les types définis par l'utilisateur1. C'est, au moins un des opérandes est de type défini par l'utilisateur. Comme avec d'autres fonctions surchargées, les opérateurs peuvent être surchargés pour un certain nombre de paramètres en une seule fois.

Pas tous les opérateurs peuvent être surchargés en C++. Parmi les opérateurs qui ne peuvent pas être surchargés sont les accesseurs . et ::, sizeof de l'opérateur, et le seul opérateur ternaire en C++, ?: Entre les opérateurs peuvent être surchargés en C++ sont les suivantes:

  • opérateurs arithmétiques: + - * / % et += -= *= /= %= (tous les infixes); + - (unaire préfixe); ++ -- (unaire préfixe et postfixe)
  • manipulation de bits: & | ^ << >> et &= |= ^= <<= >>= (tous les infixes); ~ (unaire préfixe)
  • l'algèbre booléenne: == != < > <= >= || && (tous les infixes); ! (unaire préfixe)
  • gestion de la mémoire: new new[] delete delete[]
  • la conversion implicite opérateurs
  • florilège: = [] -> , (tous les infixes); * & (tous les unaire préfixe) () (appel de fonction n-aire infixe)

Cependant, le fait que vous pouvez surcharger l'ensemble de ces ne signifie pas que vous devriez le faire. Voir les règles de base de la surcharge d'opérateur.

En C++, les opérateurs sont surchargés dans la forme de fonctions avec des noms spéciaux. Comme avec d'autres fonctions, opérateurs surchargés peuvent généralement être mis en œuvre, soit comme une fonction membre de leur opérande de gauche est de type ou non des fonctions de membre du. Si vous êtes libre de choisir ou lié à l'utilisation soit de l'un dépend de plusieurs critères.2 Un opérateur unaire @3, appliqué à un objet x, est invoqué comme operator@(x) ou x.operator@(). Un binaire opérateur infixe @, appliqué aux objets x et y, est appelé en tant que operator@(x,y) ou x.operator@(y).4

Les opérateurs qui sont mis en œuvre en tant que non-membre de fonctions sont parfois ami de leur opérande type.

1Le terme "utilisateur" peut-être un peu trompeur. C++ permet la distinction entre le bâti dans les types et les types définis par l'utilisateur. Pour l'ancien appartiennent par exemple de type int, char, double; pour les seconds appartiennent tous struct, de classe, de l'union, et les types enum, y compris celles de la bibliothèque standard, même si elles ne sont pas, comme tels, définies par l'utilisateur.

2Ce point est abordé dans une partie ultérieure de ce forum.

3L' @ n'est pas un opérateur valide en C++ c'est pourquoi je l'utilise comme un espace réservé.

4Le seul opérateur ternaire en C++ ne peut pas être surchargé et le seul n-aire opérateur doit toujours être mis en œuvre comme une fonction membre.


Continuer à Les Trois Règles de Base de la Surcharge d'Opérateur en C++.

268voto

sbi Points 100828

La Décision entre membres et Non-Membres

Les opérateurs binaires = (affectation), [] (tableau abonnement), -> (accès membres), ainsi que le n-ary () (appel de fonction) de l'opérateur, doit toujours être mis en œuvre en tant que membre de fonctions, parce que la syntaxe de la langue exige d'eux.

D'autres opérateurs peuvent être mis en œuvre, soit en tant que membres ou en tant que non-membres. Certains d'entre eux, cependant, ont généralement pour être mis en œuvre en tant que non-membre de fonctions, en raison de leur opérande de gauche ne peut pas être modifié par vous. La plus importante d'entre elles sont l'entrée et la sortie des opérateurs << et >>, dont la gauche opérandes sont des classes de flux à partir de la bibliothèque standard, vous ne pouvez pas modifier.

Pour tous les opérateurs où vous devez choisir soit de les mettre en œuvre comme une fonction membre ou non-membre de la fonction, utilisez les règles suivantes de pouce à décider:

  1. Si c'est un opérateur unaire, de l'appliquer en tant que membre de la fonction.
  2. Si un opérateur binaire traite les deux opérandes aussi (il laisse inchangé), de mettre en œuvre cet opérateur en tant que non-membre de la fonction.
  3. Si un opérateur binaire ne pas traiter à la fois de ses opérandes aussi (en général, il va changer son opérande de gauche), il pourrait être utile d'en faire un membre en fonction de son opérande de gauche du type, s'il a accès à l'opérande parties privatives.

Bien sûr, comme avec toutes les règles de base, il y a des exceptions. Si vous avez un type de

enum Month {Jan, Feb, ..., Nov, Dec}

et vous voulez surcharger l'incrémentation et de décrémentation les opérateurs pour elle, vous ne pouvez pas le faire en tant que membre de fonctions, car en C++, les types enum ne peut pas avoir les fonctions membres. Si vous avez de la surcharger une fonction libre. Et operator<() pour un modèle de classe imbriquée à l'intérieur d'un modèle de classe est beaucoup plus facile à lire et à écrire quand faire en tant que membre de la fonction inline dans la définition de la classe. Mais ceux-ci sont, en effet, à de rares exceptions près.

(Cependant, si vous faites une exception, ne pas oublier la question de l' const-ness pour l'opérande que, pour les fonctions de membres, devient l'implicite this argument. Si l'opérateur en tant que non-membre de la fonction prenne ses la plus à gauche argument comme un const de référence, le même opérateur, en tant que membre de la fonction doit avoir un const à la fin pour faire *this un const référence.)


Continuer d' opérateurs Courants de surcharge.

158voto

sbi Points 100828

La surcharge new et delete

Remarque: Cela ne traite que de la syntaxe de la surcharge new et delete, pas avec la mise en œuvre de ces opérateurs surchargés. Je pense que la sémantique de la surcharge new et delete méritent leur propre FAQ, dans le cadre du sujet de la surcharge d'opérateur je ne peux jamais faire justice.

Notions de base

En C++, quand vous écrivez une expression nouvelle, comme new T(arg) deux choses se produisent lorsque cette expression est évaluée: Premier operator new est invoquée pour obtenir de la mémoire brute, puis le constructeur approprié d' T est appelée à transformer cette matière première en mémoire dans un objet valide. De même, lorsque vous supprimez un objet, le destructeur est appelé, et la mémoire est alors retourné à l' operator delete.
C++ permet de tune de ces deux opérations: gestion de la mémoire et la construction/destruction de l'objet, à la mémoire allouée. Celui-ci est réalisé par la rédaction des constructeurs et destructeurs pour une classe. Affiner la gestion de la mémoire se fait par l'écriture de votre propre operator new et operator delete.

La première des règles de base de la surcharge d'opérateur – ne pas le faire – s'applique en particulier à la surcharge new et delete. Presque les seules raisons de surcharge de ces opérateurs sont des problèmes de performance et les contraintes de mémoire, et dans de nombreux cas, d'autres actions, comme les changements des algorithmes utilisés, fournira beaucoup plus coût/gain ratio que d'essayer de bidouiller la gestion de la mémoire.

Le C++ de la bibliothèque standard est livré avec un ensemble prédéfini new et delete opérateurs. Les plus importants sont:

void* operator new(std::size_t) throw(std::bad_alloc); 
void  operator delete(void*) throw(); 
void* operator new[](std::size_t) throw(std::bad_alloc); 
void  operator delete[](void*) throw(); 

Les deux premiers allouer/désallouer de la mémoire pour un objet, les deux derniers pour un tableau d'objets. Si vous fournissez vos propres versions de ces, ils vont pas la surcharge, mais remplacer celles de la bibliothèque standard.
Si vous surchargez operator new, vous devriez toujours aussi surcharger la correspondance operator delete, même si vous n'avez jamais l'intention de l'appeler. La raison en est que, si un constructeur lève au cours de l'évaluation d'une expression nouvelle, le système d'exécution sera le retour de la mémoire de l' operator delete correspondant à l' operator new qui a été appelée pour allouer de la mémoire pour créer l'objet. Si vous ne fournissez pas une correspondance operator delete, la valeur par défaut est appelé, qui est presque toujours tort.
Si vous surchargez new et delete, vous devriez envisager de surcharger le tableau des variantes, trop.

Placement new

C++ permet de créer de nouvelles et supprimer des opérateurs à prendre des arguments supplémentaires.
Soi-disant placement nouveau permet de créer un objet à une certaine adresse qui est transmis à:

class X { /* ... */ };
char buffer[ sizeof(X) ];
void f()
{ 
  X* p = new(buffer) X(/*...*/);
  // ... 
  p->~X(); // call destructor 
} 

La bibliothèque standard est livré avec les surcharges appropriées de la nouvelle et supprimer des opérateurs pour cela:

void* operator new(std::size_t,void* p) throw(std::bad_alloc); 
void  operator delete(void* p,void*) throw(); 
void* operator new[](std::size_t,void* p) throw(std::bad_alloc); 
void  operator delete[](void* p,void*) throw(); 

Notez que, dans l'exemple de code pour le placement de nouvelles données ci-dessus, operator delete n'est jamais appelée, à moins que le constructeur de X déclenche une exception.

Vous pouvez aussi surcharger new et delete avec d'autres arguments. Comme avec l'argument supplémentaire pour le placement de nouveaux, ces arguments sont également répertoriés entre parenthèses après le mot-clé new. Simplement pour des raisons historiques, ces variantes sont aussi souvent appelés placement nouvelles, même si leurs arguments ne sont pas pour placer un objet à une adresse spécifique.

Spécifiques à la classe new et delete

Plus couramment, vous souhaitez affiner la gestion de la mémoire parce que la mesure a montré que les instances d'une classe spécifique ou d'un groupe de classes associées, sont créés et détruits souvent et que par défaut, la gestion de la mémoire du système d'exécution, à l'écoute pour la performance générale, traite de façon inefficace dans ce cas précis. Pour améliorer cela, vous pouvez surcharge de new et delete pour une classe spécifique:

class my_class { 
  public: 
    // ... 
    void* operator new();
    void  operator delete(void*,std::size_t);
    void* operator new[](size_t);
    void  operator delete[](void*,std::size_t);
    // ... 
}; 

Surchargé par conséquent, de nouvelles et de suppression se comportent comme des fonctions membres statiques. Pour les objets d' my_class, std::size_t argument sera toujours sizeof(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 plus que cela.

Mondial de new et delete

À la surcharge de la global new et delete, il suffit de remplacer le pré-défini les opérateurs de la bibliothèque standard, avec notre propre. Cependant, c'est rarement qui doit être fait.

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