Est-il mieux en C++ pour le passage par valeur ou de passer par une référence constante?
Est-il mieux en C++ pour le passage par valeur ou de passer par une référence constante?
Je me demande ce qui est mieux que la pratique. Je me rends compte que passer par une constante référence, devrait fournir de meilleures performances dans le programme parce que vous n'êtes pas de faire une copie de la variable.
- connexes: stackoverflow.com/questions/2139224/...
Vous devez vous connecter pour publier un commentaire.
Il utilisé pour être généralement conseillée1 de utilisation passe par const ref pour tous les types, sauf pour builtin types (
char
,int
,double
, etc.), pour les itérateurs et de la fonction des objets (lambdas, des classes dérivant destd::*_function
).Cela était particulièrement vrai avant l'existence de sémantique de déplacement. La raison en est simple: si vous avez passé par valeur, une copie de l'objet a dû être faite et, sauf pour les très petits objets, c'est toujours plus cher que de passer une référence.
Avec C++11, nous avons gagné la sémantique de déplacement. En un mot, la sémantique de déplacement, de séjour que, dans certains cas, un objet peut être passé “par la valeur” sans le copier. C'est en particulier le cas lorsque l'objet que vous êtes de passage est un rvalue.
En lui-même, le déplacement d'un objet est toujours au moins aussi cher que le passage par référence. Cependant, dans de nombreux cas, une fonction interne de la copie d'un objet, en tout cas — à savoir qu'il prendra propriété de l'argument.2
Dans ces situations, nous avons les éléments suivants (simplifié) trade-off:
“Passage par valeur” provoque toujours l'objet à copier, sauf si l'objet est une rvalue. Dans le cas d'une rvalue, l'objet peut être déplacé à la place, alors que le deuxième cas est tout à coup plus “copier, puis de les déplacer”, mais “déplacer, puis (éventuellement) de se déplacer à nouveau”.
Pour les grands objets qui mettent en œuvre correcte des constructeurs de déplacement (comme les vecteurs, chaînes ...), le second cas est alors très plus efficace que la première. Par conséquent, il est recommandé de utiliser le passage par valeur si la fonction prend possession de l'argument, et si le type d'objet prend en charge efficace de déplacer.
Une note historique:
En fait, toute moderne compilateur doit être en mesure de comprendre lors du passage par valeur est cher, et de convertir implicitement l'appel à l'utilisation const ref si possible.
En théorie. Dans la pratique, les compilateurs ne peut pas toujours changer cela sans casser la fonction de l'interface binaire. Dans certains cas particuliers (lorsque la fonction est insérée) la copie sera effectivement ramené si le compilateur peut déterminer que l'objet d'origine ne sera pas changé à travers les actions dans la fonction.
Mais en général, le compilateur ne peut pas le déterminer, et l'avènement de la sémantique de déplacement dans le C++ a fait de cette optimisation, beaucoup moins.
1 E. g. dans Scott Meyers, Effective C++.
2 C'est en particulier souvent le cas pour objet les constructeurs, qui peuvent prendre des arguments et de les stocker en interne pour faire partie de la construction de l'objet d'état.
Edit: Nouvel article par david Abrahams sur les prestations du rpc-suivant:
Souhaitez vitesse? Le passage par valeur.
Passage par valeur pour les structures où la copie est bon marché a l'avantage supplémentaire que le compilateur peut supposer que les objets ne sont pas d'alias (ne sont pas les mêmes objets). En utilisant par référence le compilateur ne peut pas supposer que toujours. Exemple Simple:
le compilateur peut optimiser en
puisqu'il sait que f et g ne partage pas le même emplacement. si g est une référence (foo &), le compilateur ne pouvais pas supposer que. depuis g.j'ai pu alors être lissés par f->je et avoir une valeur de 7. ainsi, le compilateur devra re-chercher la nouvelle valeur de g.j'ai de la mémoire.
Pour plus de pratique des règles, ici, c'est un bon ensemble de règles trouvé dans Constructeurs De Déplacement article (très recommandé la lecture).
"Primitive" ci-dessus désigne essentiellement de petites types de données qui sont quelques octets de long et ne sont pas polymorphes (itérateurs, la fonction des objets, etc...) ou coûteux à copier. Dans ce papier, il y a une autre règle. L'idée est que, parfois, on veut faire une copie (dans le cas où l'argument ne peut pas être modifié), et parfois on ne veut pas (dans le cas où l'on veut utiliser l'argument lui-même dans la fonction si l'argument est un temporaire de toute façon, par exemple). Le document explique en détail comment cela peut être fait. En C++1x, cette technique peut être utilisée en mode natif avec support de la langue. Jusqu'alors, je voudrais aller avec les règles ci-dessus.
Exemples: Pour faire une chaîne de caractères en majuscules et à retourner à la version en majuscules, on doit toujours passer par valeur: On doit en prendre une copie de toute façon (on ne pouvait pas changer la const référence directement) - afin de mieux rendre aussi transparente que possible à l'appelant et de faire cette copie précoce de sorte que l'appelant peut optimiser autant que possible, comme détaillé dans le document en:
Toutefois, si vous n'avez pas besoin de changer le paramètre de toute façon, le prendre par la référence à la const:
Toutefois, si vous le but de ce paramètre est d'écrire quelque chose dans l'argument, puis la passer par des non-const de référence
__restrict__
(qui peut aussi fonctionner sur des références) que ne le font excessive des copies. Dommage que la norme C++ n'a pas adopté le C99 estrestrict
mot-clé.Dépend du type. Vous ajoutez la petite surcharge d'avoir à faire référence et de déréférencement. Pour les types avec une taille égale ou plus petite que les pointeurs qui sont à l'aide de la copie par défaut ctor, il serait probablement plus rapide de passer par valeur.
Comme il a été souligné, cela dépend du type. Pour les types de données intégrés, il est préférable de passer par valeur. Même de très petites structures, comme une paire d'entiers peuvent donner de meilleurs résultats en passant par valeur.
Voici un exemple, supposons que vous disposez d'une valeur de nombre entier et que vous souhaitez passer à une autre routine. Si cette valeur a été optimisé pour être stocké dans un registre, si vous souhaitez passer la référence, elle doit d'abord être stockées dans la mémoire et ensuite un pointeur vers la mémoire placés sur la pile pour effectuer l'appel. S'il était passé par valeur, tout ce qui est nécessaire est le registre poussé sur la pile. (Les détails sont un peu plus compliquées que ce que donne les différents systèmes d'appel et les Processeurs).
Si vous faites modèle de programmation, vous êtes souvent obligés de toujours passer par const ref puisque vous ne connaissez pas le type qui est passée. En passant des pénalités pour le passage de quelque chose de mauvais, en valeur, sont bien pires que les pénalités en cas de passage d'un type intégré par const réf.
Sonne comme vous avez obtenu votre réponse. Passage par valeur est cher, mais vous donne une copie de travailler avec si vous en avez besoin.
C'est ce que j'ai l'habitude de travailler par lors de la conception de l'interface d'un non-modèle de fonction:
Passage par valeur si la fonction ne souhaitez pas modifier le paramètre et la
la valeur n'est pas cher pour la copie (int, double, float, char, boolean, etc... Notez que std::string, std::vector, et le reste des conteneurs de la bibliothèque standard ne le sont PAS)
Passer par const pointeur si la valeur est cher pour la copie et la fonction ne
voulez pas modifier la valeur pointée et la valeur NULL est une valeur que la fonction de poignées.
Passer par des non-const pointeur si la valeur est cher pour de la copie et de la fonction
veut modifier la valeur pointée et la valeur NULL est une valeur que la fonction de poignées.
Passer par référence const lorsque la valeur est cher pour la copie et la fonction ne voulez pas modifier la valeur visée et NUL ne serait pas une valeur valide si un pointeur est utilisé à la place.
Passer par des non-const de référence lorsque la valeur est cher pour de la copie et de la fonction veut modifier la valeur visée et NUL ne serait pas une valeur valide si un pointeur est utilisé à la place.
Comme une règle de passage par référence const, c'est mieux.
Mais si vous avez besoin de modifier vous argument de fonction localement, il est mieux d'utiliser le passage par valeur.
Pour certains types de base de la performance en général les mêmes pour le passage par valeur et par référence. Fait référence à l'interne représentée par un pointeur, c'est pourquoi vous pouvez vous attendre par exemple que pour le pointeur à la fois de passage sont les mêmes en termes de performance, ou même le passage par valeur peut être plus rapide en raison de inutile de déréférencement.
En règle générale, la valeur de non-types de classe et const référence pour les classes.
Si une classe est vraiment petit, il est probablement préférable de passer par valeur, mais la différence est minime. Ce que vous voulez vraiment éviter, c'est le passage d'un nombre gigantesque de classe par valeur et de l'avoir tous les dupliqué - cela fera une énorme différence si vous êtes de passage, disons, un std::vector avec peu d'éléments.
std::vector
fait alloue ses articles sur le tas et le vecteur de l'objet lui-même ne pousse jamais. Oh, attendez. Si l'opération entraîne une copie du vecteur d'être fait, si, elle en fait aller et de dupliquer tous les éléments. Ce serait mauvais.sizeof(std::vector<int>)
est constant, mais le passage par valeur sera toujours copier le contenu en l'absence de toute compilateur intelligence.Passage par valeur pour les petits types.
Passer par const références pour les grands types (la définition de la taille peut varier entre les machines) MAIS, en C++11, le passage par valeur si vous allez consommer les données, puisque vous pouvez exploiter la sémantique de déplacement. Par exemple:
Maintenant l'appel de code:
Et un seul objet sera créé et déplacé directement dans les
name_
dans la classePerson
. Si vous passez par const référence, une copie devra être fait pour le mettre dansname_
.Simple différence :- Dans la fonction, nous avons d'entrée et de sortie paramètre , donc si votre passage d'entrée et de sortie paramètre est même alors utiliser l'appel par référence à l'autre, s'en entrée et en sortie paramètre est différente de mieux utiliser l'appel par valeur .
exemple
void amount(int account , int deposit , int total )
paramètre d'entrée : compte de dépôt
sortie paramteter: total
d'entrée et de sortie est différent d'appel par vaule
void amount(int total , int deposit )
d'entrée total de la caution
sortie totale