449 votes

Quand utiliser des références ou des pointeurs

Je comprends la syntaxe et la sémantique générale des pointeurs par rapport aux références, mais comment décider quand il est plus ou moins approprié d'utiliser des références ou des pointeurs dans une API ?

Naturellement, certaines situations nécessitent l'un ou l'autre ( operator++ a besoin d'un argument de référence), mais en général, je trouve que je préfère utiliser les pointeurs (et les pointeurs constants) car la syntaxe indique clairement que les variables sont passées de manière destructive.

Par exemple, dans le code suivant :

void add_one(int& n) { n += 1; }
void add_one(int* const n) { *n += 1; }
int main() {
  int a = 0;
  add_one(a); // Not clear that a may be modified
  add_one(&a); // 'a' is clearly being passed destructively
}

Avec le pointeur, il est toujours (plus) évident de savoir ce qui se passe, donc pour les APIs et autres où la clarté est une grande préoccupation, les pointeurs ne sont-ils pas plus appropriés que les références ? Cela signifie-t-il que les références ne devraient être utilisées que lorsque cela est nécessaire (par ex. operator++ ) ? L'un ou l'autre pose-t-il des problèmes de performance ?

ÉDITER (PÉRIMÉ) :

Outre l'autorisation des valeurs NULL et le traitement des tableaux bruts, il semble que le choix se résume à une préférence personnelle. J'ai accepté la réponse ci-dessous qui fait référence Guide de style C++ de Google Les références peuvent prêter à confusion, car elles ont une syntaxe de valeur mais une sémantique de pointeur.

En raison du travail supplémentaire nécessaire pour assainir les arguments de pointeurs qui ne devraient pas être NULL (par ex. add_one(0) appellera la version pointeur et se brisera pendant l'exécution), il est logique, du point de vue de la maintenabilité, d'utiliser des références là où un objet DOIT être présent, bien qu'il soit dommage de perdre la clarté syntaxique.

365voto

Klaim Points 24511

Utilisez la référence partout où vous le pouvez, les pointeurs partout où vous le devez.

Évitez les pointeurs jusqu'à ce que vous ne puissiez plus.

La raison en est que les pointeurs rendent les choses plus difficiles à suivre/lire, moins sûres et les manipulations bien plus dangereuses que n'importe quelle autre construction.

La règle de base est donc d'utiliser les pointeurs uniquement s'il n'y a pas d'autre choix.

Par exemple, renvoyer un pointeur vers un objet est une option valide lorsque la fonction peut renvoyer nullptr dans certains cas et il est supposé qu'il le fera. Cela dit, une meilleure option serait d'utiliser quelque chose de similaire à std::optional (nécessite C++17 ; avant cela, il y a boost::optional ).

Un autre exemple est l'utilisation de pointeurs vers la mémoire brute pour des manipulations spécifiques de la mémoire. Cela devrait être caché et localisé dans des parties très étroites du code, afin de limiter les parties dangereuses de l'ensemble de la base de code.

Dans votre exemple, il n'y a aucun intérêt à utiliser un pointeur comme argument car :

  1. si vous fournissez nullptr comme argument, vous allez dans le pays des comportements indéfinis ;
  2. la version de l'attribut de référence ne permet pas (sans astuces faciles à repérer) le problème avec 1.
  3. la version avec attribut de référence est plus simple à comprendre pour l'utilisateur : il faut fournir un objet valide, pas quelque chose qui pourrait être nul.

Si le comportement de la fonction doit fonctionner avec ou sans un objet donné, alors l'utilisation d'un pointeur comme attribut suggère que vous pouvez passer nullptr comme argument et c'est bon pour la fonction. C'est une sorte de contrat entre l'utilisateur et l'implémentation.

77voto

Andrea Bergia Points 3346

Les performances sont exactement les mêmes, car les références sont implémentées en interne comme des pointeurs. Vous n'avez donc pas à vous en préoccuper.

Il n'existe pas de convention généralement acceptée concernant l'utilisation des références et des pointeurs. Dans quelques cas, vous devez retourner ou accepter des références (constructeur de copie, par exemple), mais à part cela, vous êtes libre de faire ce que vous voulez. Une convention assez commune que j'ai rencontrée est d'utiliser les références lorsque le paramètre doit faire référence à un objet existant et les pointeurs lorsqu'une valeur NULL est acceptable.

Une convention de codage (comme Le site de Google ) prescrivent que l'on devrait toujours utiliser des pointeurs, ou des références constantes, parce que les références ont une syntaxe un peu floue : elles ont un comportement de référence mais une syntaxe de valeur.

44voto

Mahesh Points 20994

De FAQ C++ Lite -

Utilisez des références quand vous le pouvez, et des pointeurs quand vous le devez.

Les références sont généralement préférées aux pointeurs lorsque vous n'avez pas besoin de de "reseating". Cela signifie généralement que les références sont plus utiles dans l'interface publique d'une classe. l'interface publique d'une classe. Les références apparaissent généralement sur la peau d'un d'un objet, et les pointeurs à l'intérieur.

L'exception à cette règle est le cas où le paramètre ou la valeur de retour d'une fonction d'une fonction a besoin d'une référence "sentinelle" - une référence qui ne fait pas référence à un à un objet. La meilleure façon d'y parvenir est généralement de retourner/prendre un pointeur, et en donnant au pointeur NULL cette signification particulière (les références doivent toujours faire référence à des objets, pas à un pointeur NULL déréférencé).

Note : Les programmeurs C de vieille souche n'aiment pas toujours les références car elles fournissent une sémantique de référence qui n'est pas explicite dans le code du de l'appelant. Cependant, après une certaine expérience du C++, on réalise rapidement qu'il s'agit d'une forme de dissimulation de l'information, ce qui est un atout plutôt qu'un handicap. une forme de masquage de l'information, ce qui est un atout plutôt qu'un handicap. responsabilité. Par exemple, les programmeurs devraient écrire le code dans le langage du problème plutôt que dans celui de la machine. problème plutôt que dans le langage de la machine.

20voto

larsmans Points 167484

Utiliser un pointeur

  • lorsque vous pouvez avoir besoin d'un pointeur nul (puisqu'il n'existe pas de références nulles),
  • lorsque vous manipulez des tableaux "nus" (par un pointeur sur le premier élément),
  • lorsque vous retournez des objets nouvellement alloués (mais préférez un pointeur intelligent).

Utilisez une référence sinon.

Notez que la deuxième raison d'utiliser les pointeurs annule votre argument contre eux : il n'est pas plus clair d'utiliser un pointeur, puisqu'ils peuvent représenter des tableaux entiers, à moins que vous n'utilisiez T * const qui, à mon avis, n'est pas plus clair qu'une T & . (Par exemple, char * n'est généralement pas un pointeur vers un caractère unique mais une chaîne de caractères de style C).

Il n'y a pas de différence dans les performances.

18voto

bartgol Points 353

Avertissement : à part le fait que les références ne peuvent pas être NULL ni "rebondir" (ce qui signifie qu'elles ne peuvent pas changer l'objet dont elles sont l'alias), c'est vraiment une question de goût, donc je ne vais pas dire "ceci est mieux".

Cela dit, je ne suis pas d'accord avec votre dernière affirmation dans le post, en ce sens que je ne pense pas que le code perde en clarté avec les références. Dans votre exemple,

add_one(&a);

pourrait être plus clair que

add_one(a);

puisque vous savez que la valeur de a va très probablement changer. D'un autre côté, la signature de la fonction

void add_one(int* const n);

n'est pas non plus très clair : n sera-t-il un entier unique ou un tableau ? Parfois, vous n'avez accès qu'à des en-têtes (mal documentés), et des signatures telles que

foo(int* const a, int b);

ne sont pas faciles à interpréter à première vue.

A mon avis, les références sont aussi bonnes que les pointeurs lorsqu'aucune (ré)allocation ni rebondissement (dans le sens expliqué précédemment) n'est nécessaire. De plus, si un développeur n'utilise les pointeurs que pour les tableaux, les signatures de fonctions sont un peu moins ambiguës. Sans parler du fait que la syntaxe des opérateurs est beaucoup plus lisible avec les références.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X