Évitez les fuites de mémoire en C++

Jinku Hu 12 octobre 2023
  1. Utilisez l’opérateur delete pour libérer chaque new bloc mémoire alloué
  2. Utilisez std::unique_ptr pour libérer automatiquement les objets du tas
Évitez les fuites de mémoire en C++

Cet article présente plusieurs méthodes sur la façon d’éviter les fuites de mémoire dans C++.

Utilisez l’opérateur delete pour libérer chaque new bloc mémoire alloué

Les fuites de mémoire sont un problème courant pour les programmes qui interagissent directement avec l’interface de mémoire dynamique brute, ce qui oblige le programmeur à libérer chaque mémoire allouée avec la méthode correspondante. Il existe plusieurs interfaces pour la gestion de la mémoire accessibles en C++ ainsi que certains appels système spécifiques à la plate-forme, qui représentent essentiellement les fonctions de niveau le plus bas pouvant être utilisées à cette fin. Dans la plupart des cas, le programme régulier ne devrait utiliser que des fonctionnalités spécifiques à la langue comme les opérateurs new et delete pour la gestion manuelle de la mémoire ou utiliser des pointeurs intelligents pour la désallocation automatique de la mémoire.

La méthode la plus vulnérable aux fuites de mémoire consiste à utiliser les opérateurs new/delete pour gérer directement la mémoire du tas. Notez que chaque pointeur renvoyé par le new appel doit être passé à l’opérateur delete pour libérer les blocs de mémoire correspondants dans le système, sinon la mémoire pourrait s’épuiser. Par exemple, le programme suivant alloue un tableau char à 10 éléments et ne le libère pas avant la fin du programme. Ainsi, le programme souffrirait d’une fuite de mémoire.

#include <iostream>

using std::cout;
using std::endl;

constexpr int SIZE = 10;

int main() {
  char *arr = new char[SIZE];

  for (int i = 0; i < SIZE; ++i) {
    arr[i] = (char)(65 + i);
    cout << arr[i] << "; ";
  }
  cout << endl;

  return EXIT_SUCCESS;
}

Les fuites de mémoire peuvent être détectées à l’aide du programme valgrind, qui est un utilitaire en ligne de commande et peut être exécuté sur un fichier programme compilé. Notez que Valgrind est en fait un ensemble d’outils multiples, dont l’un se trouve être un utilitaire de vérification de la mémoire. La commande suivante peut être émise pour rechercher des fuites de mémoire.

valgrind --tool=memcheck --leak-check=full ./program

Une fois la vérification terminée, la sortie suivante est imprimée. Notez que, puisque nous n’avons pas appelé delete sur le pointeur arr, l’ensemble du tableau a entraîné une mémoire non libérée d’où l’enregistrement - definitely lost: 10 bytes dans le résumé de la fuite. Ce dernier est la taille réelle du tableau de caractères à 10 éléments.

==13732== HEAP SUMMARY:
==13732==     in use at exit: 10 bytes in 1 blocks
==13732==   total heap usage: 3 allocs, 2 frees, 73,738 bytes allocated
==13732==
==13732== 10 bytes in 1 blocks are definitely lost in loss record 1 of 1
==13732==    at 0x483C583: operator new[](unsigned long) (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==13732==    by 0x1091FE: main (tmp.cpp:11)
==13732==
==13732== LEAK SUMMARY:
==13732==    definitely lost: 10 bytes in 1 blocks
==13732==    indirectly lost: 0 bytes in 0 blocks
==13732==      possibly lost: 0 bytes in 0 blocks
==13732==    still reachable: 0 bytes in 0 blocks
==13732==         suppressed: 0 bytes in 0 blocks
==13732==
==13732== For lists of detected and suppressed errors, rerun with: -s
==13732== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

Utilisez std::unique_ptr pour libérer automatiquement les objets du tas

Le problème de code d’exemple précédent peut être résolu par le bouton delete [] arr; instruction avant que le programme ne se termine. Bien qu’une autre option consiste à utiliser l’un des pointeurs intelligents, par ex. std::unique_ptr. L’objet pointeur intelligent peut être initialisé avec le type donné et stocke le pointeur renvoyé par l’opérateur new. La seule différence avec le pointeur normal est que la région mémoire associée std::unique_ptr n’a pas besoin d’être delete explicitement. Au lieu de cela, le compilateur s’occupe de la désallocation une fois que l’objet sort de la portée.

#include <iostream>
#include <memory>

using std::cout;
using std::endl;

constexpr int SIZE = 10;

int main() {
  std::unique_ptr<char[]> arr(new char[SIZE]);

  for (int i = 0; i < SIZE; ++i) {
    arr[i] = (char)(65 + i);
    cout << arr[i] << "; ";
  }
  cout << endl;

  return EXIT_SUCCESS;
}
Auteur: Jinku Hu
Jinku Hu avatar Jinku Hu avatar

Founder of DelftStack.com. Jinku has worked in the robotics and automotive industries for over 8 years. He sharpened his coding skills when he needed to do the automatic testing, data collection from remote servers and report creation from the endurance test. He is from an electrical/electronics engineering background but has expanded his interest to embedded electronics, embedded programming and front-/back-end programming.

LinkedIn Facebook

Article connexe - C++ Memory