[8] Références

(Une partie de C++ FAQ Lite fr, Copyright © 1991-2002, Marshall Cline, cline@parashift.com)

Traduit de l'anglais par Jérôme Lecomte

Les FAQs de la section [8]


[8.1] Qu'est-ce qu'une référence?
Un alias (un autre nom) pour un objet.

Des références sont en particulier utilisées lors du "passage par référence"

 void swap(int& i, int& j)
 {
   int tmp = i;
   i = j;
   j = tmp;
 }

 int main()
 {
   int x, y;
   ...
   swap(x,y);
   ...
 }

Ici i et j sont des alias respectivement pour les x et y de la fonction main(). En d'autres termes, iest x - pas un pointeur sur x, ni une copie de x, mais x lui-même. Quoique vous fassiez à i, vous le faîtes à x, et vice versa.

OK. Voilà comment vous devriez penser les références en tant que programmeur. Maintenant, au risque de vous embrouiller en vous donnant une perspective différente, voici comment les références sont implantées par les compilateurs. Tout en dessous, une référence i à l'objet x est typiquement l'adresse machine de l'objet x. Mais quand le programmeur dit i++, le compilateur produit le code qui incrémente x. En particulier, les bits d'adressage que le compilateur utilise pour obtenir x ne sont pas changés. Un programmeur C pensera à ceci comme au passage par adresse, avec la variante syntaxique (1) de déplacer & de l'appeleur vers l'appelé, et (2) l'élimination des *'s. En d'autres termes, un programmeur de C pensera i comme une macro pour (*p), où p est unpointeur sur x (i.e. le compilateur déréférence automatiquement le pointeur sous-jacent; i++ est changé en (*p)++ et i = 7 est automatiquement changé en *p = 7).

Note importante: Quoiqu'une référence soit souvent implantée en utilisant une adresse dans le langage d'assemblage sous-jacent, s'il vous plaît ne pensez pas une référence comme un drôle de pointeur sur un objet. Une référence est l'objet. Ce n'est pas un pointeur sur l'objet, ni une copie de l'objet. C' est l'objet.

[ Haut | Bas | Rechercher ]


[8.2] Qu'est-ce qui se produit si j'affecte une référence?
Vous changez le referent (l'objet auquel la référence se réfère).

Rappelez-vous: la référence est la referent, ainsi changer la référence change le referent. Dans le jargon du compilateur, une référence est une "lvalue", c'est à dire quelque chose qui peut apparaître du côté gauche d'un opérateur d'affectation (operator=).

[ Haut | Bas | Rechercher ]


[8.3] Qu'est-ce qui se produit si je renvoie une référence?
L'appel de fonction peut apparaître du côté gauche d'un opérateur d'affectation (operator=).

Cette propriété peut sembler étrange de prime abord. Par exemple, personne ne pense que l'expression f() = 7 ait un sens. Cependant, si a est un objet de class Tableau, la plupart des personnes pensent que a[i] = 7 a un sens quoique a[i] n'est en réalité rien d'autre qu'un appel de fonction déguisé (Tableau::operator[](int) est appelé, qui correspond à l'opérateur d'indicage pour la classe Tableau).

class Array {
public:
	int size() const;
	float& operator[] (int index);
	...
};

int main()
{
	Array a;
	for (int i = 0; i < a.size(); ++i)
	 a[i] = 7;    // This line invokes Array::operator[](int)
	...
}

[ Haut | Bas | Rechercher ]


[8.4] Qu'est ce qu'il se passe quand je fais object.method1().method2()?

Vous enchainez les appels, c'est pourquoi cette technique est appele appels enchaines.

La premiere chose qui est executé est object.method1(). Cet appel retourne un objet, qui peut etre une reference a object (i.e., method1() peut finir par un return *this;), ou cela peut etre un objet different. Appelons l'objet retourne objectB. Puis objectB deviens le this pour l'appel method2().

L'un des usages les plus courant des appels enchaines est dans la librairie iostream. Par ex. cout << x << y marche car cout << x est une fonction qui return cout.

Un usage moins connu mais assez elegant est donne dans the Named Parameter Idiom.

[ Haut | Bas | Rechercher ]


[8.5] Comment puis-je changer le référent d'une référence pour la faire se rapporter à un objet différent?
Aucun moyen.

Vous ne pouvez pas séparer la référence du referent.

à la différence d'un pointeur, une fois qu'une référence est liée à un objet, elle ne peut pas "être repositionnée" sur un autre objet. La référence elle-même n'est pas un objet (elle n'a aucune identité; la prise de l'adresse d'une référence vous donne l'adresse du referent; rappelez-vous: la référence est son referent).

En un sens, une référence est semblable à un constpointeur comme int * const p (par opposition à un pointeur sur const comme const int * p). Malgré cette similitude apparente, s'il vous plaît ne confondez pas les références avec des pointeurs; ils ne sont pas du tout identiques.

[ Haut | Bas | Rechercher ]


[8.6] Quand dois-je utiliser des références, et quand dois-je utiliser des pointeurs?
Utilisez les références quand vous pouvez, et les pointeurs quand vous devez.

Des références sont habituellement préférées au pointeurs toutes les fois que vous n'avez pas besoin de "changer de référent" . Ceci signifie habituellement que les références sont les plus utiles dans une interface public. Les références apparaissent typiquement sur la peau d'un objet, et les pointeurs à l'intérieur.

Une exception à ce qui précède est le cas d'une fonction dont un paramètre ou la valeur de retour a besoin d'une référence "sentinelle". C'est habituellement meilleur fait en retournant/prenant un pointeur, et en donnant au pointeur NULL (ou 0) cette signification spéciale (les références devraient toujours référencer des objets, et non un pointeur NULL déréferencié).

Note: Les programmeurs C de la vieille guarde n'aiment pas parfois les références car ils apportent une sémantique de référence qui n'est pas explicite dans le code de l'appelé. Après un peu d'expérience en C++, cependant; on réalise rapidement que c'est une forme de dissimulation d'information, qui est un actif plutôt qu'un passif. En effet, les programmeurs devraient écrire le code dans le langage du problème plutôt que le langage de la machine.

[ Haut | Bas | Rechercher ]


[8.7] Qu'est ce qu'un handle d'objet? Est ce un pointeur? Est ce une reference? Est ce un pointeur sur un pointeur?

Le terme handle est utilise pour designer toutes techniques pour acceder a un autre objet — un pseudo-pointeur generalise. Le terme est intentionellement vague et ambigue.

Ambiguite peut avoir de bon cote quelquefois. Par exemple, lors des premiers designs vous ne voulez peut etre pas vous decider sur une representation pour les handles. Vous ne savez pas encore si vous allez utiliser de simple pointeurs vs. des references vs. pointeurs de pointeurs vs. references de pointeurs vs. un indice dans un tableau vs. une cle (string ou autre) qui peut etre recherchee dans une hash-table (voire une autre structure de donnee) vs. cle de database vs. toute autre technique. Si tout ce que vous savez c'est que vous aurez besoin d'un 'truc' pour identifier et acceder a un objet, vous appelez ce 'truc' un Handle.

Si votre but final c'est de permettre a un bout de code d'acceder ou de retrouver un objet, en particulier, d'une classe Fred, il vous faut passer un handle sur Fred a ce bout de code. Ce handle peut etre une string qui sera utiliser comme une cle dans une table pre-definie (e.g., une cle dans une std::map<std::string,Fred> ou une std::map<std::string,Fred*>), ou cela peut etre un entier qui sert d'index dans un tableau pre-defini (e.g., Fred* array = new Fred[maxNumFreds]), ou un simple Fred*, ou quelquechose completement different.

Les debutants pensent souvent en terme de pointeurs mais il y a de dangereux risques a utiliser des pointeurs nus. Par ex, que faire si un objet Fred doit etre copier ? Comment savoir s'il est correct de faire un delete sur les objects Fred ? Que faire si les objets Fred doivent etre, temporairement, serialises sur le disk? etc., etc. La plupart du temps on ajoute des niveaux d'indirection pour manager ce genre de situations. Par exemple, les handles peuvent etre Fred**, ou les Fred* pointes sont guarantis de ne jamais changer quand les objets Fred changent, you just update the pointed-to Fred* pointers.

L'idee c'est que l'on utilise le mot handle quand on ne sait pas encore de quoi on veux parler.

Une autre circonstance ou le mot handle est utilise et lorsque l'on veux etre vague a propos de quelquechose qui est deja fait, le terme magic cookie est parfois utilise a la place de handle dans cette situation, comme dans: "The software passes around a magic cookie that is used to uniquely identify and locate the appropriate Fred object". La raison pour laquelle on veut etre vague sur ce qui est deja implemente est que l'on veux minimiser les consequences d'un changement de representation/details du handle. Par exemple, si/quand quelqu'un change le handle d'une string a un entier, on ne veux pas aller changer un milliard de line de code.

Pour faciliter la maintenance du code quand/si les details du handle changent, ou pour rendre le code plus lisible, on encapsule le handle dans une classe. Cette classe souvent surcharge les operateurs operator-> et operator* puisque un handle agit comme un pointeur autant qu'il ressemble a un.

[ Haut | Bas | Rechercher ]


E-mail Marshall Cline Ecrire à l'auteur, au traducteur, ou en savoir plus sur la traduction.
C++ FAQ Lite fr | Table des matières | Index | A propos de l'auteur | © | Téléchargez votre propre copie ]
Dernière révision Sun Apr 13 23:54:36 PDT 2003