Pointeurs, références & allocation dynamique Rappels sur les fonctions Les spécificités du C++ Structures et classes Encapsulation des données Notions de constructeur et de destructeur Fonctions et classes amies Surcharge d'opérateur Héritage Notions de patrons de fonctions et de classes Introduction à la librairie standard STL
Compilation et directives de préprocesseur Convention d'écriture et organisation des programmes Écriture/lecture sur l'entrée/sortie standard Les membres données statiques Utilisation de enum et de typedef
Retour menu principal

Pointeurs, références & allocation dynamique de mémoire


Rappels sur les adresses et les pointeurs

Une variable prend de l’espace en mémoire. Par exemple, un entier i occupe 4 octets. L’endroit où se trouve la variable i en mémoire est l'adresse de i. Inversement, l’adresse en notation héxadécimale 0x22ff74A correpond aux 4 octets soit 32 bits 01001001001001010010101010010101010 de l’entier i. L’adresse d’une variable est donnée par &nom_de_la_variable.

De même qu’une variable peut contenir des valeurs, un pointeur est un conteneur d’adresse. Au cours de sa brève vie, une variable peut valoir 12 puis 222 puis -27 alors qu’un pointeur désignera l’adresse en mémoire soit, successivement, 0x21fd740 puis 0x22ff74A puis 0x22aa23B.

Un pointeur possède lui aussi un “type”, à savoir que l’adresse se réfère à un type de données. En outre, un pointeur occupe lui aussi de l’espace mémoire. Il prend 2 octets qui servent à conserver l’adresse. On peut donc créer des pointeurs sur des pointeurs…

La déclaration d’un pointeur se présente de la façon suivante

// Déclaration
type_du_pointeur *nom_pointeur;

tandis que son affectation s’écrira

// Affectation
nom_pointeur = adresse_de_la_variable;

Exemple:

int i;
int *pt_i;
pt_i = &i;

Allocation dynamique de mémoire via les opérateurs new et delete

La dimension d’un tableau statique, doit être connue du compilateur au moment même de la compilation. Une déclaration telle que int n; double tableau[n]; sera rejeté du fait que le compilateur ne connaît pas, au préalable, l’espace mémoire nécessaire à l’allocation.

Dans l’hypothèse où la taille d’un objet n’est connue que lors de l’exécution du programme, il est alors inévitable d'allouer dynamiquement de la mémoire, c’est-à-dire de réserver de la mémoire alors que le programme est en cours d’exécution.

En langage C, la gestion dynamique de mémoire fait appel aux fonctions malloc et free. Si comme toutes fonctions standards leur implémentation est possible en C++, leur utilisation demeure lourde et non adaptée à la programmation orientée objet. Le C++ propose deux nouveaux opérateurs new et delete pour gérer l’allocation dynamique de mémoire.

Utilisation de l’opérateur new

De même que malloc le faisait en C, new alloue une certaine quantité de mémoire pour notre tableau et renvoie un pointeur sur le début du tableau. La syntaxe est la suivante

type * pt_tableau = new type[nbr_element];

Exemple

int *pt_tableau;
unsigned int n_dimension_tableau = 666;
pt_tableau = new int[n_dimension_tableau];

Utilisation de l’opérateur delete[]

De même que free libérait l’espace alloué par malloc en C, delete[] libère l’espace mémoire alloué par l’opérateur new. Ainsi,

delete[] pt_tableau;

restaurera l’espace mémoire précédemment alloué pour 666 valeurs entières.

De manière générale, l’utilisation de l’opérateur new implique nécessairement l’utilisation de sa contrepartie delete. Dans le cas contraire, des fuites de mémoire, i.e. de la perte de mémoire libre au profit d’objet qui n’existent plus, sont à craindre et peuvent, sous certaines conditions, rendrent le système totalement inutilisable.

Référence

Une référence permet de donner deux noms différents pour une seule et même case mémoire.

Les références servent rarement directement. Le principal intérêt de la notion de référence est qu’elle permet de laisser le compilateur mettre en œuvre les instructions adéquates pour le transfert par adresse (cf. "Rappels sur les fonctions").

int i = 0;
// j est une référence de i
int &j = i;
// j et i représentent alors la même case mémoire

Une référence nécessite obligatoirement une initialisation vers une variable qu’elle va référencer. En outre, une référence pointe toujours vers le même objet; on ne pourra donc changer sa destination.