Deux façons de désigner quelque chose
Vous connaissez déjà les pointeurs : des variables qui stockent une adresse et vous permettent d'atteindre l'objet qui vit à cet endroit. Les références sont l'autre outil que C++ vous donne pour travailler indirectement avec un objet existant. Elles se recoupent suffisamment pour que les débutants ne sachent souvent pas lequel choisir, donc cette page les aligne côte à côte.
La version courte : une référence est un alias. Une fois que int& r = x; s'exécute, r est x : le même objet, un nom différent. Un pointeur est un objet distinct qui se trouve contenir l'adresse d'un autre. Cette seule différence détermine tout le reste.
Une référence est un alias
Une référence doit être liée à un objet au moment où elle est créée, et à partir de là chaque utilisation de la référence touche l'original.
Remarquez qu'il n'y a aucun * pour déréférencer ni aucun & pour « prendre l'adresse » au point d'utilisation : vous lisez et écrivez alias exactement comme un int ordinaire. Le & dans int& alias fait partie du type, ce n'est pas l'opérateur d'adresse.
Là où elles diffèrent
Les comportements ci-dessous sont la raison même pour laquelle les deux outils existent. C'est le tableau à mémoriser.
// reference pointer
// must be initialized? yes no (but should be)
// can be null? no yes (nullptr)
// can be reseated? no yes
// pointer arithmetic? no yes
// syntax to use it just the name *p or p->member
// taking address &ref == &original &p is the pointer's own address
Deux d'entre eux trompent le plus les gens. D'abord, une référence ne peut jamais être réassociée : lui affecter une valeur copie cette valeur dans l'objet désigné, cela ne fait pas pointer la référence vers quelque chose de nouveau.
Un pointeur, en revanche, est libre de pointer ailleurs à tout moment :
Ensuite, une référence ne peut jamais légalement être nulle, alors qu'un pointeur le peut. Cela rend « aucune valeur » représentable avec un pointeur mais pas avec une référence — une propriété sur laquelle vous vous appuierez constamment.
Choisir dans les paramètres de fonction
C'est là que le choix se présente le plus souvent. Quand une fonction doit lire ou modifier l'objet de l'appelant, les deux fonctionnent, mais ils signalent une intention différente.
La version avec référence (addTax(cart)) est impossible à appeler avec « rien », donc à l'intérieur de la fonction vous ne testez jamais la nullité : l'objet est garanti présent. La version avec pointeur (applyDiscount(&cart)) annonce au site d'appel, via le &, que l'argument pourrait être modifié, et elle permet à l'appelant de passer nullptr pour signifier « non applicable ». Choisissez celui dont la garantie correspond à votre fonction.
Pour les paramètres en lecture seule de types volumineux, le choix idiomatique est const T& : il évite une copie et promet de ne pas modifier. Voir paramètres de fonction pour en savoir plus sur le passage par valeur et le passage par référence.
Une règle empirique simple
En cas de doute, optez par défaut pour une référence et ne passez à un pointeur que lorsque vous avez besoin d'une capacité qui manque à la référence :
- Utilisez une référence quand l'objet existe toujours et que son identité ne change jamais — le cas courant pour les paramètres de fonction et les alias.
- Utilisez un pointeur quand l'une de ces conditions est vraie :
- « Rien » est un état valide (argument facultatif, une recherche qui peut ne trouver aucune correspondance) — un pointeur peut être
nullptr. - Vous devez pointer vers différents objets au fil du temps — un pointeur peut être réassocié.
- Vous gérez de la mémoire du tas que vous libérerez avec
delete, ou vous parcourez un tableau avec l'arithmétique des pointeurs.
- « Rien » est un état valide (argument facultatif, une recherche qui peut ne trouver aucune correspondance) — un pointeur peut être
Si aucune de ces conditions ne s'applique, une référence est le choix le plus propre et le plus sûr, car le compilateur garantit pour vous « toujours valide, jamais réassociée ».
Erreurs courantes à éviter
- S'attendre à ce que
ref = otherréassocie. À la place, cela affecte une valeur dans l'objet désigné. Les références sont liées à vie ; si vous avez besoin de réassocier, utilisez un pointeur. - Renvoyer une référence (ou un pointeur) vers une variable locale.
int& f() { int x = 5; return x; }renvoie une référence pendante :xmeurt quandfretourne et utiliser le résultat est un comportement indéfini. Le même piège frappe les pointeurs (return &x;). - Fabriquer une « référence nulle ». Écrire
int& r = *p;quandpestnullptrest un comportement indéfini dès l'instant où vous déréférencez, pas une référence « vide » sûre. Exprimez l'optionalité avec un pointeur oustd::optional. - Recourir à un pointeur par habitude. Si l'argument existe toujours et que vous ne le réassocierez jamais, une référence élimine toute une catégorie de tests de nullité et de plantages. Ne payez pas pour des capacités que vous n'utilisez pas.
Suite : La mémoire dynamique
Jusqu'ici, chaque objet que vous avez référencé ou pointé était créé automatiquement sur la pile. La page suivante, mémoire dynamique, couvre new et delete : demander de la mémoire au système d'exploitation à l'exécution, pourquoi ce sont les pointeurs (et non les références) qui la possèdent, et comment oublier de la libérer provoque des fuites.
Questions fréquentes
Quelle est la différence entre une référence et un pointeur en C++ ?
Une référence est un alias d'un objet existant : elle doit être initialisée, ne peut jamais être nulle et ne peut jamais être amenée à désigner un autre objet par la suite. Un pointeur est une variable distincte qui contient une adresse : il peut être nul, peut être réassocié pour pointer ailleurs et prend en charge l'arithmétique des pointeurs. Utilisez la syntaxe & avec les références et */-> avec les pointeurs.
Quand dois-je utiliser un pointeur plutôt qu'une référence en C++ ?
Utilisez un pointeur quand « rien » est un état valide (un argument facultatif, un résultat non trouvé), quand vous devez le réassocier pour pointer vers différents objets au fil du temps, ou quand vous possédez de la mémoire du tas que vous libérerez avec delete. Utilisez une référence quand l'objet existe toujours et ne change jamais d'identité, ce qui couvre la plupart des paramètres de fonction.
Une référence peut-elle être nulle en C++ ?
Non. Une référence valide désigne toujours un objet réel, donc vous ne testez jamais si elle est nulle. Si vous créez une référence à partir d'un pointeur nul déréférencé (int& r = *p; où p est nul), vous obtenez un comportement indéfini, pas une référence nulle. Quand vous devez exprimer « peut-être rien », utilisez un pointeur ou std::optional.