web-dev-qa-db-fra.com

différence entre un pointeur et un paramètre de référence?

Sont-ils les mêmes:

int foo(bar* p) {
  return p->someInt();
}

et

int foo(bar& r) {
  return r.someInt();
}

Ignorez le potentiel de pointeur nul. Ces deux fonctions sont-elles fonctionnellement identiques, peu importe si someInt() est virtuelle ou si on leur passe un bar ou une sous-classe de bar?

Est-ce que cela tranche quelque chose:

bar& ref = *ptr_to_bar;
64
criddell

Les références C++ ne sont pas intentionnellement spécifiées dans la norme à implémenter à l'aide de pointeurs. Une référence ressemble plus à un "synonyme" à une variable qu’à un pointeur. Cette sémantique ouvre certaines optimisations possibles pour le compilateur lorsqu'il est possible de réaliser qu'un pointeur serait une surpuissance dans certaines situations.

Quelques différences supplémentaires:

  • Vous ne pouvez pas affecter NULL à une référence. C'est une différence cruciale et la principale raison pour laquelle vous préférez l'un à l'autre.
  • Lorsque vous prenez l'adresse d'un pointeur, vous obtenez l'adresse de la variable de pointeur. Lorsque vous prenez l'adresse d'une référence, vous obtenez l'adresse de la variable référencée.
  • Vous ne pouvez pas réaffecter une référence. Une fois initialisé, il pointe vers le même objet pour toute sa vie.
65
shoosh

Ignorant tous les sucres syntaxiques et les possibilités qui peuvent être faites avec l'un et non avec l'autre et la différence entre les pointeurs et les références expliquées dans d'autres réponses (à d'autres questions) ... Oui, ces deux sont fonctionnellement exactement les mêmes! Les deux appellent la fonction et les deux gèrent également les fonctions virtuelles.

Et non, votre ligne ne tranche pas. Il s'agit simplement de lier la référence directement à l'objet pointé par un pointeur.

Quelques questions sur les raisons pour lesquelles vous voudriez utiliser l'une sur l'autre:

Au lieu d'essayer de trouver des différences moi-même, je vous délègue à ceux au cas où vous voudriez savoir.

15

La référence est un pointeur constant, c'est-à-dire que vous ne pouvez pas modifier la référence pour faire référence à un autre objet. Si vous changez, la valeur de l'objet référent change.

Pour Ex:

       int j = 10;
       int &i = j;
       int l = 20;
       i = l; // Now value of j = 20

       int *k = &j;
       k = &l;   // Value of j is still 10
12
Vinay

Oui, ils sont fonctionnellement identiques. Étant donné qu'une référence vous obligera à la définir sur un objet avant de l'utiliser, vous n'aurez pas à traiter avec des pointeurs nuls ou des pointeurs vers une mémoire invalide.

Il est également important de voir la différence sémantique:

  • Utilisez une référence lorsque vous passeriez réellement l'objet normal - mais il est si grand qu'il est plus logique de passer une référence à l'objet plutôt que d'en faire une copie (si vous ne modifiez pas l'objet qui est).
  • Utilisez un pointeur lorsque vous souhaitez traiter l'adresse mémoire plutôt que l'objet.
6
Patrick Glandien

Je n'ai pas utilisé C++ depuis longtemps, donc je ne vais même pas essayer de vraiment répondre à votre question (désolé); Cependant, Eric Lippert vient de publier un excellent article sur les pointeurs/références que je pensais que je vous indiquerais.

5
Chris Shaffer

Je ne sais pas si quelqu'un a répondu à votre 2e question cachée en bas sur le tranchage ... non, cela ne causera pas de tranchage.

Le découpage est lorsqu'un objet dérivé est affecté (copié) à un objet de classe de base - la spécialisation de la classe dérivée est "découpée". Notez que j'ai dit que object est copié, nous ne parlons pas de pointeurs copiés/assignés, mais des objets eux-mêmes.

Dans votre exemple, cela ne se produit pas. Vous dé-référencez simplement un pointeur à un objet Bar (résultant ainsi en un objet Bar) utilisé comme valeur r dans une initialisation de référence. Pas sûr d'avoir bien compris ma terminologie ...

4
Dan

Comme tout le monde l'a mentionné, dans la mise en œuvre, les références et les pointeurs sont essentiellement les mêmes. Il y a quelques mises en garde mineures:

  • Vous ne pouvez pas attribuer NULL à une référence (shoosh l'a mentionné): c'est significatif car il n'y a pas de valeur de référence "non définie" ou "invalide".

  • Vous pouvez passer une variable temporaire comme référence const , mais ce n'est pas légal de passer un pointeur vers une valeur temporaire.

Par exemple, c'est correct:

class Thingy; // assume a constructor Thingy(int,int)
void foo(const Thingy &a)
{ 
   a.DoSomething();
}

void bar( ) 
{
  foo( Thingy(1,2) );
}

mais la plupart des compilateurs se plaindront

void foo2( Thingy * a);

void bar2()
{
  foo( &Thingy(1,2) );
}
  • Prendre l'adresse d'une variable pour obtenir un pointeur oblige le compilateur à l'enregistrer en mémoire. L'attribution d'une référence à une variable locale ne fait que créer un synonyme; dans certains cas, cela peut permettre au compilateur de conserver les données dans le registre et d'éviter un load-hit-store . Cependant, cela ne s'applique qu'aux variables locales - une fois que quelque chose est passé en tant que paramètre par référence, il est impossible d'éviter de l'enregistrer dans la pile.

void foo()
{
   int a = 5;
   // this may be slightly more efficient
   int &b = a;
   printf( "%d", ++b );
   // than this
   int *c = &a;
   printf( "%d", ++(*c) );
}
  • De même, le mot-clé __ restrict ne peut pas être appliqué aux références, uniquement aux pointeurs.

  • Vous ne pouvez pas faire d'arithmétique de pointeur avec des références, alors que si vous avez un pointeur dans un tableau, l'élément suivant du tableau peut être obtenu via p + 1, une référence ne pointe que sur une chose dans toute sa vie.

3
Crashworks

Les fonctions ne sont évidemment pas "les mêmes", mais en ce qui concerne le comportement virtuel, elles se comporteront de manière similaire. En ce qui concerne le découpage, cela ne se produit que lorsque vous traitez des valeurs, pas des références ou des pointeurs.

1
anon