Les références de type sont une fonctionnalité nouvelle de C++ par rapport au langage C. Nous en verrons de très intéressants usages en programmation orientée objet, mais il nont pas de rapport direct avec elle.
Nous avons dit quune donnée est une case de la mémoire qui possède un certain type. Pour pouvoir parler de cette case, on peut soit utiliser son adresse en mémoire (qui est la méthode des pointeurs), soit faire référence directement à la donnée, ce qui est le cas quand on utilise un nom de variable.
Ainsi, après la déclaration suivante :
int i = 18;
le compilateur a réservé une case mémoire de deux octets pour la variable i
, et chaque fois que dans le programme on parle de i
, le compilateur sait quil doit utiliser cette case-là. Nous dirons que i
est une référence pour la donnée correspondante de valeur 18.
En général, un seul nom suffit pour une case mémoire. Mais dans certains cas, on a besoin dun autre nom, dune seconde référence. Pour la créer et linitialiser, on utilise lopérateur de référence &
(à ne pas confondre avec lopérateur binaire de « et » logique; il sagit dun opérateur unaire ici) :
int& j = i;
Ce qui signifie : j
est une référence sur un entier (type int&
) qui est équivalente à i
. Dès lors, tous les usages de j
dans la suite du programme seront équivalents à ceux de i
. Par exemple, si lon écrit :
int i = 18; int& j = i; i++; j++;
À la fin de ces instructions, i
et j
seront tous deux égaux à 20. Plus exactement, lunique case mémoire dénommée à la fois i
et j
contiendra la valeur 20.
Les références servent rarement directement. En général, on les utilise dans des paramètres de fonctions, ou des résultats, ainsi quen programmation orientée objet. Nous en verrons de nombreux exemples plus loin.
Signalons dautre part que lon peut indifféremment écrire :
int& j = i;
ou :
int &j = i;
La première écriture signifie « j
est du type référence sur un entier int&
, et équivaut à i » , tandis que la seconde signifie « &j
, cest-à-dire la case mémoire référencée par j
, est de type entier, et est la même que i
» ; ces deux significations sont équivalentes, si bien quon peut même écrire :
int&j = i;
ce qui est toutefois peu clair. La seconde écriture précédente est la plus utilisée en pratique, car elle permet de déclarer simultanément entier et référence sur entier, comme ceci :
int i = 18, &j = i;
On tient compte dans ce cas que les initialisations se font de gauche à droite.
Il est important de comprendre quil nexiste aucune opération sur les références, car il serait impossible au compilateur de différencier une telle opération de celle agissant sur le type de base. Cest pourquoi, lorsquon écrit j++
par exemple, lopérateur ++
sapplique à la donnée de type int
dont j
est une référence, et non au type int&
.
En particulier, il ny a pas dopérateur daffectation donnant une référence. Par conséquent, une référence ne peut être modifiée, et ne prend de valeur quau moment de son initialisation. Pour cette dernière raison, linitialisation dune référence est obligatoire. Lécriture :
int &j; // incorrect
est refusée par le compilateur (Error : Object must be initialized, lobjet doit être initialisé).
Il est parfaitement possible par contre dinitialiser une référence sur une constante, ou sur une variable dun type différent. Par exemple, on peut écrire :
int &k = 10;
Dans ce cas, une variable temporaire de type int
est créée et initialisée à 10, avant que k
ne devienne une référence de cette variable. Le code précédent équivaut donc à :
int provisoire = 10; int &k = provisoire;
Fonctionnellement, un tel code ne diffère pas de :
int k = 10;
ce qui signifie que les références sur des constantes nont aucun intérêt direct (mais elles sont très utiles indirectement dans les appels de fonctions, comme on le verra).
Le comportement est identique lorsquon initialise une référence avec une expression, ou une variable dun type différent de celui indiqué dans lexpression, même proche. Ainsi, lécriture suivante :
unsigned u = 1; int &k = u;
créera une variable intermédiaire comme précédemment; il en résulte que les modifications de k
et u
seront tout à fait indépendantes, cest pourquoi le compilateur vous prévient alors par un message dattention Warning : Temporary used to initialize 'k', une variable temporaire est utilisée pour initialiser k.
Lorsquon spécifie un changement de type, le comportement nest pas clair et dépend manifestement du bon vouloir du compilateur :
int &k = (int) v;
sera toujours accepté par Turbo C++, sans erreur ni avertissement, si la variable v
peut être convertie en un entier. Cependant, ce nest que si v
est du type unsigned
, long
ou unsigned long
, ou encore de lun des types short
, et évidemment int
, quaucune variable temporaire nest créée. Par contre, si v
est du type char
, ou de lun des types décimaux, une variable temporaire est créée mais non signalée, à la suite dun bogue dans Turbo C++... Autant éviter ces écritures par conséquent, et utiliser franchement des pointeurs. Notons que depuis le spécification 2.1 de C++, de telles écritures sont interdites.
Naturellement, on peut créer autant de types référence quil y a de types possibles : long&
, float&
, double&
, etc.
Précédent | Sommaire | Suivant |