Menu

Références en C++ : passage par référence, const& et alias

Les références C++ expliquées : comment le & dans un paramètre crée un alias, pourquoi le passage par référence évite les copies et permet à une fonction de modifier les variables de l'appelant, et quand recourir à const& et aux références plutôt qu'au retour par valeur.

Cette page contient des éditeurs exécutables - modifiez, exécutez et voyez la sortie instantanément.

Un autre nom pour la même chose

Dans la page sur les paramètres de fonction, chaque argument était copié dans la fonction. C'est cette copie qui empêche une fonction de modifier la variable de l'appelant : elle ne voit que son propre double. Une référence brise ce mur. C'est un alias : un second nom lié à une variable existante, partageant exactement la même mémoire.

Vous créez une référence avec & dans la déclaration. Une fois liée, la référence et l'original sont indiscernables :

Deux règles rendent les références sûres et prévisibles : une référence doit être initialisée au moment de sa déclaration (int& r; est une erreur de compilation) et ne peut jamais être réassignée pour désigner autre chose ensuite. Affecter à une référence écrit toujours dans ce à quoi elle a été liée à l'origine.

Passage par référence : laisser une fonction agir en retour

Le vrai bénéfice se trouve dans les fonctions. Mettez & sur un paramètre et la fonction reçoit un alias de l'argument de l'appelant au lieu d'une copie. Les modifications faites dans la fonction sont alors visibles à l'extérieur :

Retirez le & et addBonus incrémenterait une copie jetable, laissant total à 100. Ce seul caractère fait toute la différence. C'est la façon canonique d'écrire une fonction qui retourne plus d'un résultat ou qui modifie son entrée sur place. L'exemple classique est l'échange de deux variables :

Sans références, swapValues n'échangerait que des copies locales et x/y resteraient 1 2. (La bibliothèque standard fournit déjà std::swap, mais l'écrire soi-même montre exactement ce qu'apporte un paramètre par référence.)

Références const : lire vite, promettre de ne pas toucher

Le passage par référence évite aussi la copie — et pour un gros objet, cette copie peut être coûteuse. Mais un simple paramètre T& signale « je pourrais modifier ceci », ce qui est trompeur quand on veut seulement lire. La solution est const T& : vous obtenez la rapidité sans copie d'une référence et une promesse, garantie par le compilateur, que la fonction ne modifiera pas l'argument.

Une référence non const ne peut se lier qu'à une variable modifiable, mais une référence const peut aussi se lier à des littéraux et des temporaires — c'est pourquoi greet("literal works too") compile. Une règle pratique pour choisir le type d'un paramètre :

void f(int x)              // type bon marché, en lecture seule -> copiez-le simplement
void f(const string& s)   // type lourd, en lecture seule -> référence const
void f(string& s)         // vous avez l'intention de modifier l'objet de l'appelant

Utilisez const T& par défaut pour tout type de classe que vous ne faites que lire (string, vector, vos propres structures), et réservez une référence non const aux cas où vous voulez vraiment écrire en retour.

Retourner une référence

Une fonction peut aussi retourner une référence, donnant à l'appelant un alias vers quelque chose qui existe déjà. C'est courant dans le code de type conteneur — c'est ce qui permet à v[i] = 5 de fonctionner et ce que fait operator[] en coulisse :

Comme at retourne int&, l'expression d'appel at(data, 1) est elle-même une lvalue à laquelle vous pouvez affecter. Retournez plutôt un simple int et at(data, 1) = 42 ne compilerait pas — vous affecteriez à une copie temporaire.

Le gros piège : les références pendantes

Une référence ne possède rien ; elle pointe simplement vers une mémoire qui vit ailleurs. Si cette mémoire meurt alors que la référence est encore utilisée, vous avez une référence pendante, et la lire est un comportement indéfini — elle peut afficher des ordures, planter, ou sembler fonctionner jusqu'à gâcher votre journée en production. L'erreur classique est de retourner une référence vers une variable locale :

int& broken() {
    int local = 42;
    return local;   // BUG: local est détruit au retour de broken()
}                   // la référence retournée pend dans le vide

int main() {
    int& r = broken();
    cout << r << "\n";   // COMPORTEMENT INDÉFINI - lit de la mémoire morte
}

La variable local disparaît à l'instant où broken retourne, donc la référence pointe vers de l'espace de pile déjà récupéré. Ne retournez une référence que vers quelque chose qui survit à l'appel — un paramètre passé par référence, un membre de données ou une variable statique. Si la valeur est calculée à l'intérieur de la fonction, retournez par valeur et laissez le compilateur optimiser la copie. Le même piège frappe les boucles basées sur un intervalle et toute référence liée à un temporaire : ne conservez jamais une référence au-delà de la durée de vie de ce qu'elle désigne.

Suite : la surcharge de fonctions

Les références vous offrent un second réglage sur chaque paramètre — copie ou alias, mutable ou const — et ce réglage interagit directement avec le sujet suivant. Ensuite, la surcharge de fonctions vous permet de définir plusieurs fonctions portant le même nom mais avec des listes de paramètres différentes, et le compilateur choisit la bonne en faisant correspondre les types des arguments — y compris s'ils sont passés par valeur, par référence ou par référence const.

Questions fréquentes

Qu'est-ce qu'une référence en C++ ?

Une référence est un alias d'une variable existante : un autre nom pour la même mémoire. On en crée une avec & dans la déclaration : int& r = x;. Dès lors, r et x sont interchangeables ; modifier l'un modifie l'autre. Les références doivent être initialisées à la déclaration et ne peuvent jamais être réassignées pour désigner une autre variable.

Quelle est la différence entre le passage par valeur et le passage par référence en C++ ?

Le passage par valeur (void f(int x)) copie l'argument, donc la fonction travaille sur sa propre copie et la variable de l'appelant reste intacte. Le passage par référence (void f(int& x)) donne à la fonction un accès direct à la variable de l'appelant, donc les modifications sont visibles après l'appel — et aucune copie n'est faite, ce qui compte pour les gros objets.

Quand dois-je utiliser des paramètres par référence constante en C++ ?

Utilisez const T& lorsqu'une fonction n'a besoin que de lire un paramètre mais que le type est coûteux à copier (string, vector, grosses structures). Vous obtenez la rapidité sans copie d'une référence, plus la garantie du compilateur que la fonction ne modifiera pas la valeur de l'appelant. Pour des types bon marché comme int ou double, le simple passage par valeur est plus simple et tout aussi rapide.

Coddy programming languages illustration

Apprendre à coder avec Coddy

COMMENCER