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);
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);
nothing
. Ceci peut être utilisé pour fournir des arguments optionnels.string s = &str1 + &str2;
En utilisant des pointeurs.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."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++:
const
les références vous permettent d'avoir une sémantique pas à pas tout en évitant la copieSon 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).
J'aime le raisonnement d'un article de "cplusplus.com:"
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.)
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.
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.
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.
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.
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 ).
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.
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:
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.