Intro

En C++, les pointeurs comme en C existent toujours mais une seconde façon d’éviter de copier un objet que l’on passe en paramètre existe. Il s’agit de références.

Ce sont des pointeurs dont l’utilisation est plus simple et sur lesquels il n’est pas possible de faire d’opérations arithmétiques.

On peut trouver une référence aux mêmes endroit que l’on pourrait trouver un pointeur. Quand on déclare un pointeur on utilise une étoile * dans la déclaration, entre le type et le nom. Pour les références c’est pareil sauf qu’on remplace l’étoile par &.

Enfin, quand on déclare une référence on est obligés de l’initialiser immédiatement. C’est justement pour éviter les plantages.

Pour initialiser une référence, contrairement au pointeurs, on a pas besoin d’utiliser une adresse avec l’opérateur &. On dit que la référence capture la variable à laquelle elle fait référence.

Pour utiliser une référence, on a pas besoin de la déréférencer avec l’opérateur *.

Exemple avec un pointeur

int main(void)
{
	std::string str{"abc"};
	std::string *ref_str{&str};
	*ref_str = "hello";
	std::cout << str;	
}

Exemple avec une référence

int main(void)
{
	std::string str{"abc"};
	std::string &ref_str{str};
	ref_str = "hello";
	std::cout << str;	
}

Davantage d’exemples

Voici trois exemples qui font quasiment la même chose avec une copie, un pointeur et une référence.

Copie

Dans cet exemple, la chaîne est copiée, ce qui signifie qu’on utilise plus de mémoire que nécessaire.

void edit_and_print(std::string str)
{
    str[1] = 'a';
    std::cout << str << "\n";
}

int main(void)
{
    std::string str{"hello"};
    edit_and_print(str);
    std::cout << "after: " << str << '\n';
}

Sortie :

hallo
after: hello

Pointer

Si on passe l’adresse de la chaîne, on évite de la copier, mais on doit la déréférence pour chaque opération, ce qui n’est pas toujours pratique.

On encourt aussi le risque que quelqu’un nous passe un pointeur nul, ce qui ferait planter le programme.

void edit_and_print_ptr(std::string *str_ptr)
{
    (*str_ptr)[1] = 'a';
    std::cout << *str_ptr << "\n";
}

int main(void)
{
    std::string str{"hello"};
    edit_and_print_ptr(&str);
    std::cout << "after: " << str << '\n';
}

Sortie :

hallo
after: hallo

Ref

Avec une référence, on a l’avantage du pointeur qui est que l’on ne fait pas une copie de la chaîne et on peut la modifier dans la fonction appelée, sans avoir le désavantage de devoir faire des déréférences ou de risquer le plantage.

void edit_and_print_ref(std::string &str_ref)
{
    str_ref[1] = 'a';
    std::cout << str_ref << "\n";
}

int main(void)
{
    std::string str{"hello"};
    edit_and_print_ref(str);
    std::cout << "after: " << str << '\n';
}

Sortie :

hallo
after: hallo