Références et pointeurs
Ce que tu vas apprendre
- Choisir entre passage par valeur, par référence et par référence constante.
- Lire et écrire un pointeur : adresse, déréférencement,
nullptr. - Savoir quand préférer une référence à un pointeur.
Le problème de la copie
Par défaut, une fonction reçoit une copie de ses arguments. Modifier la copie ne change pas l'original :
void doubler(int n) {
n = n * 2;
}
int main() {
int portions{4};
doubler(portions);
std::cout << portions << "\n"; // affiche 4
}
Pour un int, la copie ne coûte rien. Pour un vecteur de milliers d'éléments, elle coûte cher.
Les références
Une référence est un second nom pour une variable existante. Elle se déclare avec & après le type :
void doubler(int& n) {
n = n * 2;
}
int main() {
int portions{4};
doubler(portions);
std::cout << portions << "\n"; // affiche 8
}
Ici n désigne directement portions. Aucune copie n'est faite, et la fonction peut modifier l'original.
Une référence doit être initialisée dès sa création et ne peut jamais être réaffectée à une autre variable.
La référence constante
Pour éviter la copie sans autoriser la modification, ajoute const :
void afficher(const std::vector<std::string>& ingredients) {
for (const auto& i : ingredients) {
std::cout << i << "\n";
}
}
C'est la forme la plus courante pour passer des chaînes et des conteneurs en lecture seule.
| Paramètre | Copie ? | Modifie l'original ? |
|---|---|---|
int n | Oui | Non |
int& n | Non | Oui |
const int& n | Non | Non |
Règle pratique : petits types (int, double, char, bool) par valeur, gros objets par référence constante, et par référence simple seulement pour modifier.
Adresses et pointeurs
Chaque variable vit à une adresse en mémoire. Un pointeur est une variable qui stocke une adresse. Le * dans la déclaration indique un pointeur, l'opérateur & récupère l'adresse :
int duree{45};
int* p = &duree;
std::cout << p << "\n"; // une adresse, par exemple 0x7ffd...
std::cout << *p << "\n"; // 45
*p = 50; // duree vaut maintenant 50
Utiliser *p s'appelle le déréférencement : on accède à la valeur située à l'adresse. Attention, le symbole * a donc deux sens selon l'endroit où il apparaît.
nullptr
Un pointeur peut ne pointer sur rien. On le déclare alors avec nullptr :
int* p = nullptr;
if (p != nullptr) {
std::cout << *p << "\n";
}
Pointeur ou référence ?
Les deux désignent un objet sans le copier. Leurs différences :
| Référence | Pointeur | |
|---|---|---|
| Peut être « vide » | Non | Oui (nullptr) |
| Peut changer de cible | Non | Oui |
| Syntaxe d'accès | Comme une variable | *p ou p-> |
Choisis la référence par défaut. Prends un pointeur seulement quand l'absence de valeur est possible ou que la cible doit changer.
Pointeur vers un objet
Pour accéder à un membre d'un objet via un pointeur, on utilise la flèche ->, raccourci de (*p).. Exemple avec une chaîne :
std::string titre{"Quiche lorraine"};
std::string* p = &titre;
std::cout << p->size() << "\n"; // 15
int* ne gèrent pas la mémoire eux-mêmes. Pour posséder un objet alloué dynamiquement, on utilise des pointeurs intelligents, vus au cours 06.Pièges classiques
- Retourner une référence vers une variable locale : elle est détruite à la fin de la fonction, la référence devient invalide.
- Garder un pointeur vers un élément d'un
std::vectorpuis ajouter des éléments : le vecteur peut déplacer ses données en mémoire. - Oublier que
int* a, b;déclare un pointeuraet un entierb.
À retenir
- Par défaut, un argument est copié ; une référence
T&évite la copie et permet la modification. const T&passe un gros objet en lecture seule, sans copie.- Un pointeur contient une adresse :
&xla donne,*plit la valeur pointée. - Initialise un pointeur à
nullptret teste-le avant de le déréférencer. - Une référence ne peut pas être nulle ni changer de cible, c'est le choix par défaut.
p->membreaccède à un membre d'un objet pointé.
Pour aller plus loin
Sources officielles
Ensuite
std::string et std::vector
Manipuler du texte avec std::string et des listes d'éléments avec std::vector, les deux outils de base de la bibliothèque standard.
Classes, constructeurs et RAII
Définir ses propres types avec les classes, initialiser un objet avec un constructeur et libérer ses ressources automatiquement grâce au destructeur (RAII).
- Projet
- Lignes de code
- Niveau
- Le quartier
- Appartement
- C++
- Pièce
- 4/7
- Plan n°
- cpp/04.references-et-pointeurs