Chargement...

Gestion de mémoire dynamique : new et delete

Allocations sur le tas, usage sûr de new/delete et préambule vers RAII.

En C++, la mémoire est divisée en deux régions principales : la pile (Stack) et le tas (Heap). Tandis que la pile est gérée automatiquement, la mémoire du tas est contrôlée manuellement par le programmeur. La gestion dynamique de la mémoire est utilisée pour allouer et libérer de la mémoire pendant l’exécution du programme. Dans cet article, nous allons examiner en détail l’utilisation de new et delete pour la gestion dynamique de la mémoire.


1. Différence entre la pile et le tas

CaractéristiquePile (automatique)Tas (dynamique)
GestionAutomatique (par le compilateur)Manuelle (par le programmeur)
Durée de vieSupprimée automatiquement à la fin de la fonctionReste jusqu’à être supprimée manuellement
VitesseTrès rapidePlus lente (allocation dynamique)
Taille de la mémoireLimitéeBeaucoup plus grande
UtilisationVariables localesObjets et tableaux dynamiques

2. Allocation de mémoire avec le mot-clé new

L’opérateur new alloue dynamiquement de la mémoire sur le tas et retourne l’adresse de la zone allouée. Cette adresse est généralement assignée à une variable pointeur.


#include <iostream>
using namespace std;

int main() {
    int *ptr = new int; // alloue de la mémoire pour un entier
    *ptr = 42;
    cout << "Valeur : " << *ptr << endl;

    delete ptr; // libère la mémoire
    ptr = nullptr; // sécurise le pointeur
}

Remarque : la mémoire allouée avec new doit être libérée manuellement avec delete. Sinon, une fuite de mémoire (memory leak) se produit.


3. Tableaux dynamiques avec new

Pour créer un tableau dynamique, on utilise new[]. Sa taille peut être déterminée à l’exécution.


#include <iostream>
using namespace std;

int main() {
    int n;
    cout << "Combien d’éléments ? ";
    cin >> n;

    int *tableau = new int[n]; // crée un tableau de n éléments

    for (int i = 0; i < n; i++)
        tableau[i] = (i + 1) * 10;

    cout << "Tableau : ";
    for (int i = 0; i < n; i++)
        cout << tableau[i] << " ";

    delete[] tableau; // libère la mémoire
    tableau = nullptr;
}

L’utilisation de delete[] est essentielle ; l’opérateur delete simple est réservé aux variables uniques, pas aux tableaux.


4. Étapes de l’allocation et de la libération de mémoire dynamique

  1. new → alloue de la mémoire sur le tas.
  2. Pointeur → stocke l’adresse de la mémoire allouée.
  3. Les données sont traitées ou remplies.
  4. delete / delete[] → libère la mémoire allouée.
  5. pointeur = nullptr → empêche les pointeurs pendants (dangling pointers).

5. Qu’est-ce qu’une fuite de mémoire ?

Lorsqu’une mémoire allouée dynamiquement n’est pas libérée, des blocs de mémoire inutilisés s’accumulent. Ce phénomène s’appelle une fuite de mémoire et peut provoquer de graves problèmes de performance dans les programmes à long terme.


void MauvaisExemple() {
    int *p = new int(10);
    // delete p; // Oublié ! Fuite de mémoire
}

Solution : toujours libérer la mémoire avec delete.


6. nullptr et sécurité mémoire

Avant C++11, les pointeurs nuls étaient généralement représentés par NULL. En C++ moderne, il est recommandé d’utiliser le mot-clé nullptr, plus sûr et typé.


int *p = nullptr;

if (p == nullptr) {
    cout << "Le pointeur ne pointe vers aucune adresse." << endl;
}

L’utilisation de nullptr aide à éviter les accès mémoire invalides et les erreurs de segmentation.


7. Utiliser la mémoire dynamique avec des fonctions

La mémoire peut être créée dynamiquement et passée à une fonction. Cette approche est particulièrement utile avec les tableaux dynamiques.


void Remplir(int *p, int n) {
    for (int i = 0; i < n; i++)
        p[i] = (i + 1) * 2;
}

int main() {
    int *tableau = new int[5];
    Remplir(tableau, 5);

    for (int i = 0; i < 5; i++)
        cout << tableau[i] << " ";

    delete[] tableau;
}

8. Créer un objet avec new

L’opérateur new peut être utilisé non seulement pour les types de base, mais aussi pour créer des objets de classe.


#include <iostream>
using namespace std;

class Etudiant {
public:
    string nom;
    Etudiant(string n) : nom(n) {
        cout << nom << " créé." << endl;
    }
    ~Etudiant() {
        cout << nom << " supprimé." << endl;
    }
};

int main() {
    Etudiant *p = new Etudiant("Ali");
    delete p; // le destructeur est appelé
}

Supprimer un objet créé avec new appelle automatiquement son destructeur.


9. Pointeurs intelligents (approche C++ moderne)

Avec C++11, les pointeurs intelligents (smart pointers) ont été introduits comme alternative à la gestion manuelle de la mémoire avec delete. Ils permettent une gestion automatique de la mémoire.


#include <iostream>
#include <memory>
using namespace std;

int main() {
    unique_ptr<int> p = make_unique<int>(100);
    cout << *p << endl; // 100
}

Les pointeurs intelligents libèrent automatiquement la mémoire lorsque l’objet sort du champ de portée, éliminant ainsi le risque de fuite de mémoire.


10. TL;DR

  • new → alloue de la mémoire sur le tas et retourne son adresse.
  • delete → libère la mémoire (variable unique).
  • new[] / delete[] → utilisés pour les tableaux dynamiques.
  • nullptr → définition sûre d’un pointeur nul.
  • Pour éviter les fuites de mémoire, chaque new doit être accompagné d’un delete.
  • std::unique_ptr et std::shared_ptr sont des alternatives modernes.
  • Tous les exemples peuvent être compilés dans Visual Studio 2022 ou GCC 11+.

Articles connexes