C++ Mecanisme references

Fermé
betsprite - 13 janv. 2010 à 22:05
Char Snipeur Messages postés 9696 Date d'inscription vendredi 23 avril 2004 Statut Contributeur Dernière intervention 3 octobre 2023 - 14 janv. 2010 à 08:39
Bonsoir tout le monde,

J'ai fait pas mal de recherche sur les références mais voila il y a quelque chose qui m'échappe.

J'ai bien compris la différence avec les pointeurs comme quoi un pointeur pouvait pointer vers une adresse puis ensuite changer d'adresse ... alors que la référence ne peut pas changer de valeur.

Ce que je ne comprends pas bien, c'est comment marche la référence ? Comment, lorsqu'on appelle une référence dans une fonction, le programme se rappelle des modifications apportées sur l'objet référencé en paramètre ?

La référence va tout de même chercher aussi en mémoire pour pouvoir changer les valeurs "à la source" ?

Merci d'avance pour vos réponses et bonne soirée ;)

6 réponses

Pacorabanix Messages postés 3248 Date d'inscription jeudi 23 août 2007 Statut Membre Dernière intervention 19 mai 2013 660
13 janv. 2010 à 22:19
pour répondre à cette question il faut comprendre comment le c++ (comme la plupart des langages) utilise la mémoire.

Une variable = une case mémoire d'une certaine taille (selon le type).

Une "case mémoire", c'est comme un appartement dans un quartier résidentiel. Il faut connaitre son adresse, on ne peut pas sonner à toutes les portes pour trouver la personne qu'on cherche.

L'adresse de cette case mémoire est donnée par le système d'exploitation (le programme demande à avoir une case mémoire libre pour sa variable au système, ce dernier lui en donne une selon les disponibilités).

Ex :

//début du programme :
// au démarrage, une case mémoire est allouée qui représente la variable a
int a=5;

printf("valeur de a = %d", a); // affiche 5

//ici le programme lit la valeur de a (il lit sa case mémoire),
// il ajoute 3, 
// puis il stocke ce résultat dans la variable a,
//c'est à dire dans la case mémoire dont on parlait plus haut
a = a + 3;

printf("valeur de a = %d", a); // affiche 8



Lorsqu'on fait appel à une fonction, les variables utilisées dans la fonction (y compris les arguments !) sont allouées (== les cases mémoires correspondant à ces variables sont allouées) à l'appel de la fonction, puis à la fin de la fonction toutes ces cases mémoires sont libérées (=désallouées).

C'est pour cela que :
void modif(int arg) {
  //ici une nouvelle case mémoire est crée pour "arg"
  //cette case mémoire est modifiée
  // mais ce n'est pas celle de la variable a
  // donc après la fonction la variable a n'est en fait pas modifiée
  arg = arg + 3;
}

int main(void) {
  int a = 5;
  printf("valeur de a = %d", a); // affiche 5

modif(a);
printf("valeur de a = %d", a); // affiche 5</code>
}</code>

affiche deux fois la même chose. Tu as compris cela ? Le détail avec les cases mémoires ?
0
Oui j'ai compris ton code, et c'est d'ailleurs pour cela que les pouinteurs sont necessaires en paramètre d'une fonction, pour pouvoir directement faire les modif au coeur de la mémoire et que ces modif depassent la simple fonction ou elles sont faites.

Mais mon probleme se situe plus au niveau de la notion de référence.

En fait, je sais que la référence, à la différence du pointeur, ne peut prendre qu'une valeur fixe.

Mais comment sa marche ? une référence sa "pointe" quand meme a une adresse pour changer des parametres au coeur de la memoire comme le pointeur non ?

Et dans ce cas, pourquoi la référence n'est pas une adresse de mémoire ?

Je ne comprends pas pourquoi si on a la méthode :

*********************

void bavarderCamarade ( Etudiant &etudiantLambda) {

etudiantLambda.bavarder();

}

// Etudiant étant une class

Etudiant Jean;
Etudiant Damien;

Damien.bavarderCamarade( Jean);

************************

Comment sa se fait que lorsqu'on déclare "Jean" comme valeur et lorsqu"on met "Jean" en parametre se soit la même syntaxe alors que dans un cas c'est une variable donnée, et dans l'autre c'est une référence ?


Avec les pointeurs au moins on avait "*pt etudiant" pour prévenir que c'etait un pointeur et la on avait pas la meme syntaxe :

****************

Etudiant Jean;


Damien.bavarderCamarade( &Jean);

********



Voila j'ai du mal à comprendre la syntaxe de référence, à bien comprendre le fait que la variable Jean et la référence Jean ont la même syntaxe ...
0
Pacorabanix Messages postés 3248 Date d'inscription jeudi 23 août 2007 Statut Membre Dernière intervention 19 mai 2013 660
13 janv. 2010 à 22:40
ok je comprends ton "angoisse" ^^

une référence est bien une adresse mémoire. oui.

mais le nom d'une variable, pour le C++, ce n'est rien d'autre que son adresse mémoire aussi (plus ou moins!)...
Disons que pour faire simple : c'est la syntaxe du C++, le standard, qui est fait comme ça. On aurait pu faire un autre choix (en Java c'est différent par exemple).

EDIT : lorsque l'argument est spécifié "par référence" à l'aide du "&", le C++ fait comme un grand la distinction entre l'adresse de la mémoire et sa valeur.

tu aurais pu, j'espère ne pas dire de bêtise, faire :

void fonction ( int* arg) { .... }

et appeler cette fonction ainsi :

int b;
fonction(&b);
0
Ok :p

Mais si les variables définies étaient des adresses mémoires on aurait pas le problème de la destruction des variables à la fin d'une méthode.

Et si c'était déjà une adresse on aurait pas besoin des pointeurs et références :p.

Enfin bon je vais essayer d'y réfléchir à tête reposée lol.

Merci en tout cas ! Bonne soirée à toi ;)
0
Pacorabanix Messages postés 3248 Date d'inscription jeudi 23 août 2007 Statut Membre Dernière intervention 19 mai 2013 660 > betsprite
13 janv. 2010 à 22:46
mais si... n'oublie pas qu'à chaque exécution du programme l'adresse change. Il faut donc un "symbole" pour s'y retrouver (le nom de variable).

Avec ceci :

int a;

le compilateur C++ sait qu'il doit écrire dans le programme le code qui demande l'allocation d'une zone de mémoire pour un int.


mais si tu as besoin d'un nombre variable de cases mémoires, car tu ne sais pas encore ce que tu vas avoir dans ton programme, alors on peut allouer dynamiquement des cases mémoires, à la demande et non plus automatiquement à chaque exécution, afin de ne pas se faire avoir par des variables prédéfinies. Mais là, on doit utiliser new et les pointeurs.


PS : oui tête reposée c'est mieux ^^ bonne soirée à toi aussi.
0
betsprite > Pacorabanix Messages postés 3248 Date d'inscription jeudi 23 août 2007 Statut Membre Dernière intervention 19 mai 2013
13 janv. 2010 à 22:56
Ok je comprends. Oui par exemple pour les allocations dynamiques avec la méthode malloc pour les tableaux ...

Mais en fait je me demandais, quand je fais :

// Damien.bavarderCamarade( Jean);

qui a pour prototypage :

// void bavarderCamarade( Etudiant &etudiantLambda);

Aprés avoir demandé une place en mémoire pour l'étudiant Jean :

// Etudiant Jean;

Quand je le met dans la méthode bavarderCamarade en paramètre, au lieu de faire une copie de la variable Jean et de faire les modifications dessus, on reconnait directement la référence Jean sans passer par une copie c'est ça non ?
0
Pacorabanix Messages postés 3248 Date d'inscription jeudi 23 août 2007 Statut Membre Dernière intervention 19 mai 2013 660 > betsprite
13 janv. 2010 à 23:00
oui. C'est à dire qu'il y a une nouvelle case mémoire crée dans la pocédure, et cette case mémoire est remplie avec une référence (=une adresse mémoire) qui pointe directement sur le véritable "objet" Jean.

PS : malloc (et free) c'est du C, préfère new (et delete) pour le C++
0
betsprite > Pacorabanix Messages postés 3248 Date d'inscription jeudi 23 août 2007 Statut Membre Dernière intervention 19 mai 2013
13 janv. 2010 à 23:05
Et la création de ce nouvel espace mémoire à lieu quand ? puisque la référence n'est définie que dans le paramètre de la méthode pas avant ... ( avant on a juste dit "Etudiant Damien;" donc -> garde un espace memoire pour une variable de type Etudiant quelque part)
0
loupius Messages postés 697 Date d'inscription dimanche 1 novembre 2009 Statut Membre Dernière intervention 31 décembre 2017 148
14 janv. 2010 à 00:12
Pour compléter les réponses précédentes, il ne faut pas oublier que cette notion de référence a été introduite en C++ pour faciliter l'utilisation des pointeurs; le but est de pouvoir modifier des paramètres dans une fonction sans s'en apercevoir... ou presque car il faut quand même le préciser lors de la déclaration des paramètres de la fonction.
Voici un exemple:
#include <iostream>
#include <cstdlib>
using namespace std;

void f_valeur (int a)
{
  a++;    // on incrémente une copie de 'a' du main
}

void f_reference (int& a)
{
  a++;    // on incrémente le 'a' du main
}

void f_pointeur (int* a)
{
  (*a)++; // on incrémente le 'a' du main
}

int main(int argc, char *argv[])
{
  int a=1;

  cout << "Avant f_valeur : a=" <<  a << endl;
  f_valeur (a);
  cout << "Après f_valeur : a=" <<  a << endl;
  f_reference (a);
  cout << "Après f_reference : a=" <<  a << endl;
  f_pointeur (&a);
  cout << "Après f_pointeur  : a=" <<  a << endl;

  return EXIT_SUCCESS;
}

[loupius@p3000]$ g++ -Wall essai.cpp
[loupius@p3000]$ ./a.out
Avant f_valeur : a=1
Après f_valeur : a=1
Après f_reference : a=2
Après f_pointeur  : a=3
[loupius@p3000]$
'f_reference' se comporte exactement comme 'f_pointeur', mais on n'a pas besoin d'utiliser la notion de pointeur; en fait tout ce 'maquillage' est réalisé par le compilateur.
Bonne réflexion.
0
Meric pour vos informations en tout cas ça va beaucoup m'aider !

En fait ce qui me dérange le plus c'est qu'un référence peut être à la fois vu comme une variable "synonyme" d'une autre variable :

****************
int age = 21;
int &referenceSurAge = age;

referenceSurAge = 40; // Ici on a l'impression de manipuler une variable.
****************

et comme adresse :

****************

void incrementeAge ( int &referenceAge); // prototypage

incrementeAge ( referenceSurAge); // ici on a l'impression de manipuler une adresse.

****************

-----------------------------------------------------------------------------------------------------------------------------

Ormis ce problème, j'aurais une tout autre question :

Si on fait :

**************

Damien = new Etudiant();

***************

On a un espace alloué en memoire pour l'objet Damien de la class Etudiant.
En fait, d'aprés ce que j'ai compris, Damien est alors en fait un pointeur pointant à l'adresse ou la valeur est Damien.

Donc si on fait ça sa devrait etre correcte non ?

**********

Etudiant *pointeurSurEtudiant = Damien; // c'est exact ou faut mettre : Etudiant *pointeurSurEtudiant = &Damien ?

***********

Et si on fait alors :

prototypage : void bavarderCamarade ( & referenceEtudiant); // reference sur un objet de la class Etudiant.

bavarderCamarade ( Damien);

C'est bon de faire sa ? malgré que Damien soit un pointeur et que l'on demande une référence??
0

Vous n’avez pas trouvé la réponse que vous recherchez ?

Posez votre question
loupius Messages postés 697 Date d'inscription dimanche 1 novembre 2009 Statut Membre Dernière intervention 31 décembre 2017 148
14 janv. 2010 à 00:58
En fait la déclaration est:
Etudiant* damien = new Etudiant;
On utilise 'damien' avec un petit 'd' car on réserve les majuscules aux noms de classe; on ne met pas de parenthèses si on a rien a mettre dedans; on précise que 'damien' est un pointeur (puisque l'opérateur 'new' renvoie un pointeur).

Etudiant *pointeurSurEtudiant = Damien;
est évidemment correct.

void bavarderCamarade (& referenceEtudiant)
bavarderCamarade (Damien);

N'est pas correct, consulte le post n° 11. Il faut évidemment mettre: bavarderCamarade (*Damien)
Bonne réflexion.
0
Ok merci beaucoup pour ces précisions !

Donc en gros c'est impossible de faire un prototypage avec en paramètre une reference et appeler ensuite la méthode avec en parametre un pointeur ?

Il faut pour cela redefinir le prototypage avec un pointeur en parametre c'est bien sa ?

Merci beaucoup encore !
0
loupius Messages postés 697 Date d'inscription dimanche 1 novembre 2009 Statut Membre Dernière intervention 31 décembre 2017 148 > betsprite
14 janv. 2010 à 01:18
Relis ceci (post n° 13), je vais le mettre en gras:
Etudiant* damien = new Etudiant;
void bavarderCamarade (Etudiant& referenceEtudiant)
bavarderCamarade (damien);

N'est pas correct, consulte le post n° 11.
Il faut évidemment mettre:
bavarderCamarade (*damien);
Donc c'est possible sans rien redéfinir.
Bonne réflexion.
0
Char Snipeur Messages postés 9696 Date d'inscription vendredi 23 avril 2004 Statut Contributeur Dernière intervention 3 octobre 2023 1 297
14 janv. 2010 à 08:39
Salut.
Si tu avait utilisé d'autres langages, tu saurait aussi que le passage par copie à une fonction n'est pas la règle.
Par exemple en Fortran les variables sont automatiquement passés par référence et donc modifiable.
Dans les bouquin de C++ il est bien préciser que ce passage par référence a été fait pour simplifier l'appel de fonction ET diminuer les temps d'appel (copier une adresse est moins long que de copier tout un objet très gros) MAIS ils soulignent aussi le fait que cela faisait une certaine ambiguïté et qu'il convenait que le nom de la fonction annonce assez clairement qu'elle modifie la variable passée en paramètre.
Tu peux avoir un comportement équivalent à la référence en déclarant ta fonction ainsi :
void camarade(Etudiant*const a){...}
"a" sera alors un pointeur que l'on ne peut pas modifier, mais dont on peut modifier la valeur.
0