web-dev-qa-db-fra.com

Y a-t-il des avantages à passer d'un pointeur à un autre en passant par une référence en C ++?

Quels sont les avantages de passer d'un pointeur à un autre en passant d'une référence en C++?

Dernièrement, j'ai vu un certain nombre d'exemples qui ont choisi de passer des arguments de fonction par des pointeurs au lieu de passer par référence. Y a-t-il des avantages à le faire?

Exemple:

func(Sprite *x);

avec un appel de

func(&mySprite);

vs.

func(Sprite &x);

avec un appel de

func(mySprite);
215
Matt Pascoe

Un pointeur peut recevoir un paramètre NULL, un paramètre de référence ne peut pas. S'il y a une chance que vous souhaitiez passer "pas d'objet", utilisez un pointeur au lieu d'une référence.

De plus, passer par pointeur vous permet de voir explicitement sur le site de l'appel si l'objet est passé par valeur ou par référence:

// Is mySprite passed by value or by reference?  You can't tell 
// without looking at the definition of func()
func(mySprite);

// func2 passes "by pointer" - no need to look up function definition
func2(&mySprite);
209
Adam Rosenfield

En passant par le pointeur

  • Le demandeur doit prendre l'adresse -> pas transparente
  • Une valeur 0 peut être fournie pour signifier nothing. Ceci peut être utilisé pour fournir des arguments optionnels.

Pass par référence

  • L'appelant passe juste l'objet -> transparent. Doit être utilisé pour la surcharge de l'opérateur, car la surcharge pour les types de pointeur n'est pas possible (les pointeurs sont des types intégrés). Donc, vous ne pouvez pas faire string s = &str1 + &str2; En utilisant des pointeurs.
  • Pas de 0 valeurs possibles -> La fonction appelée n'a pas à les vérifier
  • La référence à const accepte également les temporaries: void f(const T& t); ... f(T(a, b, c));, les pointeurs ne peuvent pas être utilisés comme cela car vous ne pouvez pas prendre l'adresse d'un temporaire.
  • Enfin, les références sont plus faciles à utiliser -> moins de risques de bugs.
232

"Assez de corde pour se tirer une balle dans le pied" d'Allen Holub énumère les 2 règles suivantes:

120. Reference arguments should always be `const`
121. Never use references as outputs, use pointers

Il énumère plusieurs raisons pour lesquelles des références ont été ajoutées à C++:

  • ils sont nécessaires pour définir les constructeurs de copie
  • ils sont nécessaires pour les surcharges de l'opérateur
  • const les références vous permettent d'avoir une sémantique pas à pas tout en évitant la copie

Son point principal est que les références ne doivent pas être utilisées comme paramètres de "sortie" car sur le site de l'appel, rien ne permet de savoir si le paramètre est une référence ou une valeur. Sa règle est donc de n'utiliser que const références comme arguments.

Personnellement, je pense que c'est une bonne règle empirique, car cela le rend plus clair lorsqu'un paramètre est un paramètre de sortie ou non. Cependant, même si je suis personnellement d’accord avec cela en général, je me permets d’être influencé par les opinions des autres membres de mon équipe s’ils défendent les paramètres de sortie comme références (certains développeurs les apprécient immensément).

61
Michael Burr

J'aime le raisonnement d'un article de "cplusplus.com:"

  1. Passer par la valeur lorsque la fonction ne veut pas modifier le paramètre et que la valeur est facile à copier (ints, doubles, char, bool, etc ... types simples. Std :: string, std :: vector et tous les autres STL les conteneurs ne sont PAS des types simples.)

  2. Passer par le pointeur const lorsque la valeur est chère à copier ET la fonction ne veut pas modifier la valeur pointée vers ET NULL est une valeur attendue valide que la fonction gère.

  3. Passer par le pointeur non-const lorsque la valeur est chère à copier ET la fonction veut modifier la valeur pointée sur ET NULL est une valeur attendue valide que la fonction gère.

  4. Passer en référence const lorsque la valeur est chère à copier ET la fonction ne veut pas modifier la valeur référencée ET NULL ne serait pas une valeur valide si un pointeur était utilisé à la place.

  5. Passer par une référence non-cont lorsque la valeur est chère à copier ET la fonction veut modifier la valeur référencée ET NULL ne serait pas une valeur valide si un pointeur était utilisé à la place.

  6. Lors de l'écriture de fonctions de modèle, il n'y a pas de réponse claire, car il y a quelques compromis à considérer qui dépassent le cadre de la présente discussion, mais il suffit de dire que la plupart des fonctions de modèle prennent leurs paramètres par valeur ou par référence (const) Cependant, étant donné que la syntaxe des itérateurs est similaire à celle des pointeurs (astérisque de "déréférencement"), toute fonction de modèle qui attend des itérateurs en tant qu'arguments acceptera également les pointeurs par défaut (et ne recherchera pas NULL car le concept d'itérateur NULL a une syntaxe différente ).

http://www.cplusplus.com/articles/z6vU7k9E/

Ce que j'en déduis, c'est que la principale différence entre le choix d'utiliser un pointeur ou un paramètre de référence est si NULL est une valeur acceptable. C'est ça.

Que la valeur soit entrée, sortie, modifiable, etc. devrait être dans la documentation/commentaires sur la fonction, après tout.

60
R. Navega

Précisions sur les articles précédents:


Les références sont ET NON une garantie d'obtenir un pointeur non nul. (Bien que nous les traitons souvent comme tels.)

Alors que le code est horriblement mauvais, comme dans vous sortir derrière le bosquet mauvais , ce qui suit va compiler et exécuter: (Au moins sous mon compilateur.)

bool test( int & a)
{
  return (&a) == (int *) NULL;
}

int
main()
{
  int * i = (int *)NULL;
  cout << ( test(*i) ) << endl;
};

Le vrai problème que j'ai avec les références réside dans les autres programmeurs, désormais nommés [~ # ~] idiots [~ # ~] , qui alloue dans le constructeur, libère dans le destructeur, et ne fournit pas de constructeur de copie ou d'opérateur = ().

Tout à coup, il y a un monde de différences entre foo (BAR bar) et foo (BAR & = barre) . (L'opération de copie au niveau du bit automatique est invoquée. La déviation dans le destructeur est invoquée deux fois.)

Heureusement, les compilateurs modernes reprendront cette double répartition du même pointeur. Il y a 15 ans, ils ne l'ont pas fait. (Sous gcc/g ++, utilisez setenv MALLOC_CHECK_ 0 pour revenir aux anciennes méthodes.) Résultat, sous DEC UNIX, dans la même mémoire étant allouée à deux objets. Beaucoup de plaisir à déboguer là-bas ...


Plus concrètement:

  • Les références masquent le fait que vous modifiez des données stockées ailleurs.
  • Il est facile de confondre une référence avec un objet copié.
  • Les pointeurs le rendent évident!
8
Mr.Ree

Pas vraiment. En interne, le passage par référence est effectué en transmettant essentiellement l'adresse de l'objet référencé. Donc, il n'y a pas vraiment de gains d'efficacité à obtenir en passant un pointeur.

Passer par référence présente toutefois un avantage. Vous avez la garantie d'avoir une instance de tout type d'objet/type transmis. Si vous passez un pointeur, vous courez le risque de recevoir un pointeur NULL. En utilisant passe par référence, vous poussez un niveau implicite NULL-check up à l'appelant de votre fonction.

3
Brian