Gestion de la mémoire dynamique en C++ avec les pointeurs intelligents

Mémoire dynamique en C++

Les programmes précédents utilisaient des objets avec des durées de vie prédéfinies :

  • Objets globaux : alloués au lancement, détruits à l'arrêt
  • Objets locaux automatiques : créés à l'entrée du bloc, détruits à la sortie
  • Objets statiques locaux : alloués avant première utilisation, détruits à l'arrêt

Pour dépasser ces limitations, C++ permet l'allocation dynamique d'objets dont la durée de vie est indépendante de leur scope. Leur destruction nécessite une libération explicite.

La libération correcte est une source majeure d'erreurs. La bibliothèque standard fournit donc des pointeurs intelligents qui garantissent une libération automatique.

Pointeurs intelligents

Deux types principaux existent :

  • shared_ptr : permet le partage de la propriété
  • unique_ptr : propriété exclusive

Définis dans l'en-tête <memory>, ils se comportent comme des pointeurs classiques mais gèrent automatiquement la libération.

shared_ptr

Création recommandée avec make_shared :

auto ptrDouble = make_shared<double>(3.14);
auto ptrChaine = make_shared<string>("Bonjour");
auto ptrVide = make_shared<vector<int>>();

Le comptage de références gère automatiquement la durée de vie :

auto principal = make_shared<int>(100);
auto secondaire(principal); // Compteur = 2
principal.reset(); // Compteur = 1
// L'objet persiste tant que secondaire existe

Gestion directe de mémoire

Les opérateurs new et delete sont risqués :

double* nouveauDouble = new double(2.718); 
// ... utilisation ...
delete nouveauDouble; // Libération manuelle obligatoire

Problèmes fréquents :

  1. Oubli de delete → fuite mémoire
  2. Utilisation après libération → comportement indéfini
  3. Double libération → corruption mémoire

Combinaison avec pointeurs bruts

Initialisation possible avec new :

shared_ptr<complexe> ptrComplexe(new complexe(1, 2));

Pièges à éviter :

  • Ne pas utiliser le même pointeur brut pour plusieurs smart pointers
  • Ne pas appeler delete sur un pointeur obtenu par get()
  • Ne pas initialiser un smart pointer avec get()

Gession de ressources non-mémoire

Les pointeurs intelligents peuvent gérer d'autres ressources :

struct ConnexionBDD;
ConnexionBDD* ouvrirConnexion();
void fermerConnexion(ConnexionBDD*);

void utiliserBDD() {
  ConnexionBDD* bdd = ouvrirConnexion();
  shared_ptr<ConnexionBDD> gestionnaire(bdd, fermerConnexion);
  // La connexion se fermera automatiquement
}

unique_ptr

Pointeur à propriété exclusive :

unique_ptr<vector<int>> ptrVec(new vector<int>{1,2,3});
// Transfert de propriété
unique_ptr<vector<int>> nouveauPtr = move(ptrVec);

Gestion de tibleaux :

unique_ptr<int[]> tableau(new int[10]);
for(int i = 0; i < 10; ++i) {
  tableau[i] = i * i;
}

weak_ptr

Référence faible sans affecter la durée de vie :

shared_ptr<string> principal = make_shared<string>("Test");
weak_ptr<string> observateur = principal;

if(auto acces = observateur.lock()) {
  // Accès sécurisé si l'objet existe toujours
}

Tableaux dynamiques

Allocation avec new[] :

long* tabLongs = new long[100];
// Initialisation
delete[] tabLongs; // Libération obligatoire

Alternative moderne :

vector<unique_ptr<string>> mots;
mots.push_back(make_unique<string>("dynamique"));

Allocateur personnalisé

Sépare allocation et construction :

allocator<string> alloc;
auto debut = alloc.allocate(5);
auto fin = uninitialized_fill_n(debut, 5, "valeur");

for(auto it = debut; it != fin; ++it) {
  alloc.destroy(it);
}
alloc.deallocate(debut, 5);

Étiquettes: C++ mémoire dynamique pointeurs intelligents shared_ptr unique_ptr

Publié le 12 juillet à 03h40