Contenu du cours
Pointeurs Intelligents C++
Pointeurs Intelligents C++
Comprendre les Pointeurs et la Mémoire Dynamique
Qu'est-ce qu'un pointeur
Les pointeurs sont un concept essentiel dans le monde de C++. Au lieu de stocker des données réelles, les pointeurs stockent des adresses mémoire, permettant aux développeurs d'accéder et de manipuler efficacement différentes parties de la mémoire.
Remarque
Vous pouvez considérer les pointeurs comme des coordonnées GPS. Ils nous indiquent des emplacements spécifiques dans la mémoire et nous permettent d'accéder aux données stockées à ces emplacements.
Lorsque vous déclarez un pointeur, vous créez une variable qui contient l'adresse mémoire d'une autre variable. Par exemple, vous pouvez créer un pointeur vers un entier comme ceci :
ptr
contient l'adresse mémoire de l'entiermyValue
.- Le signe
&
est utilisé pour obtenir l'adresse mémoire de la variable avec laquelle il est utilisé. &myValue
récupère l'adresse mémoire de la variablemyValue
, qui est ensuite stockée dans le pointeurptr
.
Pointeurs nuls
En C++, null
ou nullptr
sont des mots-clés spéciaux utilisés pour représenter un pointeur nul. Un pointeur nul ne pointe vers aucune adresse mémoire valide. C'est essentiellement un pointeur sans cible.
Remarque
Nous pouvons également initialiser le pointeur entier à
null
(ounullptr
). Nous faisons cela lorsque nous ne voulons pas associer le pointeur à une valeur spécifique immédiatement.
Réaffectation des pointeurs
Les pointeurs peuvent être réaffectés :
Arithmétique des pointeurs
L'arithmétique des pointeurs est un aspect fascinant des pointeurs. Elle vous permet de parcourir la mémoire en incrémentant ou décrémentant l'adresse détenue par un pointeur.
Par exemple, considérez le code suivant où nous créons un tableau d'entiers puis définissons un pointeur pour contenir l'adresse du tableau. Étant donné qu'un tableau contient plusieurs éléments, un pointeur, par défaut, stocke l'adresse du premier élément, qui dans ce cas est l'entier 1.
Pour accéder au deuxième élément, nous pouvons simplement incrémenter le pointeur de 1. Ensuite, nous le déréférençons en utilisant l'opérateur (*
). Le déréférencement renvoie la valeur présente à l'adresse mémoire détenue par le pointeur (l'entier 2 dans notre cas). Suivez les commentaires dans le code ci-dessus pour savoir ce qui se passe !
Allocation dynamique de mémoire
Il existe deux types de mémoire disponibles pour une utilisation dans un programme C++ : pile et tas. La mémoire de la pile est limitée en taille. Elle est bien adaptée pour les petites variables de courte durée qui ne sont pas nécessaires au-delà de leur portée. Lorsqu'une variable sur la pile sort de sa portée, elle est automatiquement détruite.
Cependant, lorsque vous avez besoin de mémoire qui persiste plus longtemps que sa portée, ou qui nécessite plus d'espace, vous vous tournez vers le tas. Le tas est un réservoir de mémoire beaucoup plus grand où votre programme peut demander de la mémoire à l'exécution. Cela se fait généralement en utilisant le mot-clé new
:
Ici, nous avons alloué de la mémoire pour un entier sur le tas et stocké son adresse dans un pointeur. Le problème délicat avec la mémoire du tas est qu'elle n'est pas libérée automatiquement. C'est la responsabilité du développeur de la désallouer lorsqu'il a terminé. Pour chaque new
, il doit y avoir un delete
correspondant.
Fuites de mémoire et pointeurs pendants
Lorsque vous introduisez des pointeurs dans votre code C++, vous entrez dans le domaine de la gestion manuelle de la mémoire. La gestion manuelle de la mémoire nécessite de libérer la mémoire lorsqu'elle n'est plus nécessaire. Cela peut conduire à des pièges courants : les fuites de mémoire et les pointeurs pendants.
Fuites de mémoire
Les fuites de mémoire se produisent généralement lorsque vous oubliez de désallouer la mémoire. Cette mémoire reste réservée, ce qui fait que votre programme perd de la mémoire au fil du temps.
Exemple
Vous créez des objets dynamiques dans une boucle, mais ne les supprimez jamais, cela entraînera une fuite de mémoire.
Pointeurs pendants
Les pointeurs pendants se produisent lorsqu'un pointeur continue de pointer vers une mémoire qui a été désallouée. C'est comme avoir une carte au trésor qui vous mène à un endroit (adresse mémoire) où le trésor (données) a déjà été pris (détruit).
main
#include <iostream> int main() { int* ptr = new int; // dynamically allocate an integer *ptr = 42; // assign a value to the allocated memory std::cout << *ptr << std::endl; // This will output 42 delete ptr; // deallocate the memory std::cout << *ptr << std::endl; // ptr is a dangling pointer! }
Dans le code ci-dessus, nous essayons d'accéder à un pointeur après avoir appelé delete
dessus, ce qui désalloue la mémoire à laquelle il pointait. Après la suppression, le pointeur devient un pointeur pendant, car il contient toujours l'adresse d'une mémoire qui n'existe plus.
Accéder à un pointeur pendant peut entraîner un comportement imprévisible ou même des plantages, car la mémoire à laquelle il pointe n'est plus valide. Pour éviter les pointeurs pendants, il est important de définir un pointeur sur "null" après avoir appelé delete dessus. Ensuite, avant d'accéder à la valeur de ce pointeur, il est impératif de vérifier s'il est nul ou non. Considérez le code suivant :
main
#include <iostream> int main() { // Dynamically allocate an integer int* ptr = new int; // Assign a value to the allocated memory *ptr = 42; // Output the value, which will be 42 std::cout << *ptr << std::endl; // Free the allocated memory to avoid memory leaks delete ptr; // Set the pointer to null ptr = nullptr; // Check whether the pointer is not null if (ptr != nullptr) // Access and output the value, which won't execute std::cout << *ptr << std::endl; }
Merci pour vos commentaires !