Gestion de la mémoire dynamique en C++ : new, delete, et Smart Pointers

Gestion de la mémoire dynamique en C++ : new, delete, et Smart Pointers

Amine Abidi - Lead Software Engineer C++/Qt - Associé PointerLab

Publié par Amine Abidi - Lead Software Engineer C++/Qt - Associé PointerLab

Introduction

La gestion de la mémoire est un aspect essentiel de la programmation, en particulier en C++, où le développeur est souvent responsable de l'allocation et de la libération de la mémoire. Contrairement à d'autres langages comme Java ou Python, C++ ne dispose pas de garbage collector automatique, ce qui signifie qu'une mauvaise gestion peut entraîner des fuites de mémoire ou des comportements indéfinis.

Dans cet article, nous allons explorer :

  • Comment gérer la mémoire dynamique avec new et delete.
  • Les problèmes potentiels liés à cette approche.
  • Les bonnes pratiques modernes avec les smart pointers de la bibliothèque standard.

1. Comprendre la mémoire dynamique en C++

En C++, la mémoire est divisée en trois parties principales :

  1. Mémoire statique : Utilisée pour les variables globales et statiques, allouée au démarrage du programme.
  2. Mémoire automatique (pile) : Utilisée pour les variables locales, gérée automatiquement par le compilateur.
  3. Mémoire dynamique (tas) : Allouée et libérée manuellement par le programmeur.

La mémoire dynamique est gérée à l'aide des opérateurs new et delete.

#include <iostream>

int main() {
int* ptr = new int(42); // Allocation dynamique
std::cout << "Valeur : " << *ptr << std::endl;

    delete ptr;  // Libération de la mémoire
    return 0;

}

2. Risques associés à la mémoire dynamique

Bien que new et delete soient puissants, ils présentent des risques :

1. Fuites de mémoire

Si vous oubliez de libérer la mémoire allouée, celle-ci reste occupée inutilement, ce qui peut épuiser les ressources du système. Cela peut être particulièrement problématique si une exception interrompt l'exécution avant que la mémoire ne soit libérée correctement.
Pour en savoir plus sur la gestion des erreurs et les exceptions, consultez notre article : Exceptions et gestion des erreurs en C++.

#include <iostream>

void fuiteMemoire() {
    int* ptr = new int(10);  // Mémoire allouée
    // Pas de delete ici : fuite de mémoire !
}

int main() {
    fuiteMemoire();
    return 0;
}

2. Dangling pointers

Un pointeur peut devenir invalide si la mémoire qu'il pointe est libérée.

#include <iostream>

int main() {
    int* ptr = new int(10);
    delete ptr;  // Mémoire libérée

    // Accès à un pointeur non valide (dangling pointer)
    std::cout << "Valeur : " << *ptr << std::endl;  // Comportement indéfini
    return 0;
}

3. Double libération

Libérer deux fois la même mémoire peut provoquer des erreurs graves.

#include <iostream>

int main() {
    int* ptr = new int(10);
    delete ptr;  // Libération de la mémoire
    delete ptr;  // Erreur ! Double libération

    return 0;
}

3. Améliorations avec les Smart Pointers

Avec les évolutions modernes du langage, il est préférable d'utiliser les smart pointers de la STL, qui gèrent automatiquement la durée de vie des objets. Pour en savoir plus sur la Standard Template Library (STL) et ses fonctionnalités, consultez notre article : Introduction à la STL en C++.

Les types principaux de Smart Pointers :

  1. std::unique_ptr

    • Gestion exclusive de la mémoire.
    • Non copiable, mais transférable avec std::move.
#include <iostream>
#include <memory>

int main() {
    std::unique_ptr<int> ptr = std::make_unique<int>(42);  // Allocation sans fuite
    std::cout << "Valeur : " << *ptr << std::endl;

    // La mémoire est automatiquement libérée à la fin de la portée
    return 0;
}
  1. std::shared_ptr
  • Permet un partage d'ownership entre plusieurs pointeurs.
  • Utilise un compteur de référence pour gérer la mémoire.
#include <iostream>
#include <memory>

int main() {
    std::shared_ptr<int> ptr1 = std::make_shared<int>(42);
    std::shared_ptr<int> ptr2 = ptr1;  // Partage du même pointeur

    std::cout << "Valeur via ptr1 : " << *ptr1 << std::endl;
    std::cout << "Valeur via ptr2 : " << *ptr2 << std::endl;

    // La mémoire est libérée lorsque le compteur atteint zéro
    return 0;
}
  1. std::weak_ptr
  • Référence non-propriétaire utilisée pour éviter les cycles avec std::shared_ptr.
#include <iostream>
#include <memory>

struct Node {
    std::shared_ptr<Node> next;
    std::weak_ptr<Node> prev;  // Référence faible pour éviter les cycles
};

int main() {
    auto node1 = std::make_shared<Node>();
    auto node2 = std::make_shared<Node>();

    node1->next = node2;
    node2->prev = node1;  // Pas de cycle grâce à weak_ptr

    return 0;
}

4. Comparaison entre new/delete et Smart Pointers

Aspect new / delete Smart Pointers
Gestion automatique ❌ Non ✅ Oui
Risque de fuite de mémoire ✅ Elevé ❌ Réduit
Complexité ✅ Manuelle ✅ Simplifiée

5. Conseils pratiques pour la gestion de la mémoire

  • Toujours préférer les smart pointers à l'utilisation directe de new et delete.
  • Utiliser les conteneurs de la STL (std::vector, std::map, etc.) pour éviter l'allocation manuelle. Pour découvrir les principaux conteneurs et leur utilisation, consultez notre article : Introduction à la STL en C++.
  • Utiliser des outils comme Valgrind ou AddressSanitizer pour détecter les fuites de mémoire.

Conclusion

La gestion de la mémoire dynamique est un aspect fondamental de la programmation en C++. Bien que new et delete soient toujours disponibles, les smart pointers offrent une alternative moderne et sûre pour gérer la mémoire. En adoptant ces pratiques, vous pouvez écrire du code C++ plus robuste, lisible et maintenable.

À propos de pointerlab

Chez pointerlab, nous mettons notre expertise unique en C, C++, Qt, OpenGL et bien d'autres technologies au service des entreprises. Que vous ayez besoin de solutions sur mesure pour la simulation 3D, les systèmes embarqués, ou des projets innovants dans des secteurs comme l’aéronautique, le médical ou l’énergie, nous vous accompagnons dans le développement de logiciels métiers.

Vous avez un projet ? Contactez-nous dès maintenant pour discuter de vos besoins et découvrir comment nous pouvons collaborer.

Vous êtes ingénieur logiciel C++ ? Rejoignez-nous ! Découvrez nos opportunités ici et contribuez à façonner l’avenir des logiciels d'excellence.

Rejoignez la communauté C++ France sur Discord !

Développez votre réseau et boostez votre carrière avec la communauté C++ France