Quels sont les avantages du passage par pointeur par rapport au passage par 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 à faire cela?
Exemple:
func(SPRITE *x);
avec un appel de
func(&mySprite);
contre.
func(SPRITE &x);
avec un appel de
func(mySprite);
c++
pointers
parameter-passing
pass-by-reference
Matt Pascoe
la source
la source
new
de créer un pointeur et les problèmes de propriété qui en résultent.Réponses:
Un pointeur peut recevoir un paramètre NULL, pas un paramètre de référence. S'il y a une chance que vous souhaitiez passer "aucun objet", utilisez un pointeur au lieu d'une référence.
De plus, le passage par un pointeur vous permet de voir explicitement sur le site d'appel si l'objet est passé par valeur ou par référence:
la source
func2 passes by reference
. Bien que j'apprécie que vous vouliez dire qu'il passe "par référence" dans une perspective de haut niveau, implémenté en passant un pointeur dans une perspective au niveau du code, cela était très déroutant (voir stackoverflow.com/questions/13382356/… ).func(int& a)
n'est pas C valide dans aucune version de la norme. Vous compilez probablement vos fichiers en C ++ par accident.En passant par le pointeur
nothing
. Cela peut être utilisé pour fournir des arguments facultatifs.Passer par référence
string s = &str1 + &str2;
utiliser de pointeurs.void f(const T& t); ... f(T(a, b, c));
pointeurs ne peuvent pas être utilisés comme ça car vous ne pouvez pas prendre l'adresse d'un temporaire.la source
J'aime le raisonnement d'un article de "cplusplus.com:"
Ce que je retiens de cela, c'est que la principale différence entre le choix d'utiliser un pointeur ou un paramètre de référence est que NULL est une valeur acceptable. C'est tout.
Que la valeur soit entrée, sortie, modifiable, etc. devrait être dans la documentation / commentaires sur la fonction, après tout.
la source
«Enough Rope to Shoot Yourself in the Foot» d'Allen Holub énumère les 2 règles suivantes:
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 passe-par-valeur tout en évitant une copieSon principal argument 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 n'indique si le paramètre est une référence ou un paramètre de valeur. Donc, sa règle est de n'utiliser que des
const
références comme arguments.Personnellement, je pense que c'est une bonne règle de base car cela rend plus clair quand un paramètre est un paramètre de sortie ou non. Cependant, bien que je sois personnellement d'accord avec cela en général, je me laisse influencer par les opinions des autres membres de mon équipe s'ils plaident pour des paramètres de sortie comme références (certains développeurs les aiment énormément).
la source
Précisions sur les postes précédents:
Les références ne garantissent PAS l' obtention d'un pointeur non nul. (Bien que nous les traitions souvent comme tels.)
Bien que le code soit horriblement mauvais, comme pour vous faire sortir du mauvais code de Woodshed , ce qui suit se compilera et s'exécutera: (Au moins sous mon compilateur.)
Le vrai problème que j'ai avec les références réside dans d'autres programmeurs, désormais appelés IDIOTS , qui allouent dans le constructeur, désallouent dans le destructeur et ne fournissent pas de constructeur de copie ou d'opérateur = ().
Soudain, il y a un monde de différence entre foo (BAR bar) et foo (BAR & bar) . (L'opération de copie automatique au niveau du bit est invoquée. La désallocation dans le destructeur est invoquée deux fois.)
Heureusement, les compilateurs modernes prendront cette double désallocation du même pointeur. Il y a 15 ans, ils ne l'ont pas fait. (Sous gcc / g ++, utilisez setenv MALLOC_CHECK_ 0 pour revoir les anciennes méthodes.) Il en résulte, sous DEC UNIX, que la même mémoire est allouée à deux objets différents. Beaucoup de débogage amusant là-bas ...
Plus concrètement:
la source
*i
, votre programme a un comportement indéfini. Par exemple, le compilateur peut voir ce code et supposer "OK, ce code a un comportement indéfini dans tous les chemins de code, donc toute cette fonction doit être inaccessible." Ensuite, il supposera que toutes les branches menant à cette fonction ne sont pas prises. Il s'agit d'une optimisation régulièrement effectuée.La plupart des réponses ici ne parviennent pas à résoudre l'ambiguïté inhérente à la présence d'un pointeur brut dans une signature de fonction, en termes d'expression de l'intention. Les problèmes sont les suivants:
L'appelant ne sait pas si le pointeur pointe vers un seul objet ou vers le début d'un "tableau" d'objets.
L'appelant ne sait pas si le pointeur "possède" la mémoire vers laquelle il pointe. IE, que la fonction libère ou non la mémoire. (
foo(new int)
- Est-ce une fuite de mémoire?).L'appelant ne sait pas s'il
nullptr
peut ou non être transmis en toute sécurité à la fonction.Tous ces problèmes sont résolus par des références:
Les références se réfèrent toujours à un seul objet.
Les références ne possèdent jamais la mémoire à laquelle elles se réfèrent, elles ne sont qu'une vue dans la mémoire.
Les références ne peuvent pas être nulles.
Cela fait des références un bien meilleur candidat pour une utilisation générale. Cependant, les références ne sont pas parfaites - il y a quelques problèmes majeurs à considérer.
&
opérateur pour montrer que nous passons bien un pointeur. Par exemple,int a = 5; foo(a);
il n'est pas du tout clair ici que a est passé par référence et pourrait être modifié.std::optional<T&>
n'est pas valide (pour de bonnes raisons), les pointeurs nous donnent la nullité que vous souhaitez.Il semble donc que lorsque nous voulons une référence nullable avec une indirection explicite, nous devrions atteindre un
T*
droit? Faux!Abstractions
Dans notre désespoir de nullité, nous pouvons atteindre
T*
et simplement ignorer toutes les lacunes et l'ambiguïté sémantique énumérées précédemment. Au lieu de cela, nous devrions rechercher ce que C ++ fait le mieux: une abstraction. Si nous écrivons simplement une classe qui entoure un pointeur, nous gagnons l'expressivité, ainsi que la nullité et l'indirection explicite.C'est l'interface la plus simple que j'ai pu trouver, mais elle fait le travail efficacement. Il permet d'initialiser la référence, de vérifier si une valeur existe et d'accéder à la valeur. On peut l'utiliser comme ça:
Nous avons atteint nos objectifs! Voyons maintenant les avantages, par rapport au pointeur brut.
nullptr
peut être transmis, car l'auteur de la fonction demande explicitement unoptional_ref
Nous pourrions rendre l'interface plus complexe à partir d'ici, comme l'ajout d'opérateurs d'égalité, une interface monadique
get_or
etmap
, une méthode qui obtient la valeur ou lève une exception,constexpr
support. Vous pouvez le faire.En conclusion, au lieu d'utiliser des pointeurs bruts, expliquez ce que ces pointeurs signifient réellement dans votre code, et tirez parti d'une abstraction de bibliothèque standard ou écrivez la vôtre. Cela améliorera considérablement votre code.
la source
Pas vraiment. En interne, le passage par référence est effectué en passant essentiellement l'adresse de l'objet référencé. Donc, il n'y a vraiment aucun gain d'efficacité à faire en passant un pointeur.
Passer par référence présente cependant un avantage. Vous êtes assuré d'avoir une instance de n'importe quel objet / type transmis. Si vous passez un pointeur, vous courez le risque de recevoir un pointeur NULL. En utilisant pass-by-reference, vous poussez un contrôle NULL implicite d'un niveau vers l'appelant de votre fonction.
la source