Le quartierC++ C++Pièce 6/7Avancé

Mémoire, pointeurs intelligents et bibliothèque standard

Distinguer pile et tas, gérer la propriété avec unique_ptr et shared_ptr, puis utiliser les algorithmes et std::map de la bibliothèque standard.

Ce que tu vas apprendre

  • La différence entre la pile et le tas.
  • Posséder un objet avec std::unique_ptr ou std::shared_ptr.
  • Trier et chercher avec les algorithmes, et associer clés et valeurs avec std::map.

Pile et tas

Les variables locales vivent sur la pile : une zone rapide, libérée automatiquement à la fin du bloc. Sa taille est limitée et la durée de vie de ses objets est liée à la portée.

Le tas est une zone plus vaste, où l'on réserve de la mémoire à la demande pour qu'elle survive à la fonction qui l'a créée. C'est à toi d'indiquer quand la libérer, ou plutôt à un objet qui s'en charge.

PileTas
LibérationAutomatique, en fin de blocManuelle ou via un pointeur intelligent
TailleLimitéeGrande
UsageVariables localesObjets à longue durée de vie ou de taille variable

Pourquoi éviter new et delete

On peut allouer avec new et libérer avec delete, mais l'oubli d'un delete provoque une fuite mémoire, et un delete en trop plante le programme. Un retour anticipé ou une erreur au mauvais endroit suffit à les provoquer.

Depuis C++11, on confie cette tâche à des pointeurs intelligents, des objets RAII qui libèrent la mémoire dans leur destructeur. Ils se trouvent dans l'en-tête <memory>.

std::unique_ptr

Un std::unique_ptr possède seul un objet. Quand il disparaît, l'objet est détruit :

#include <memory>
#include <string>

struct Recette {
    std::string titre;
};

int main() {
    auto r = std::make_unique<Recette>(Recette{"Gratin"});
    std::cout << r->titre << "\n";
}   // la Recette est détruite ici, sans delete

std::make_unique est disponible depuis C++14 : compile avec -std=c++17 ou plus. Un unique_ptr ne se copie pas, puisqu'un seul propriétaire est permis. On transfère la propriété avec std::move :

auto a = std::make_unique<Recette>(Recette{"Soupe"});
auto b = std::move(a);   // b possède, a est vide (nullptr)

std::shared_ptr

Un std::shared_ptr autorise plusieurs propriétaires. L'objet est détruit quand le dernier d'entre eux disparaît, grâce à un compteur de références :

auto a = std::make_shared<Recette>(Recette{"Ratatouille"});
auto b = a;   // copie permise, deux propriétaires
std::cout << a.use_count() << "\n";   // 2
Choisis unique_ptr par défaut. Passe à shared_ptr seulement si la propriété est réellement partagée, car le compteur a un coût.

Les algorithmes

L'en-tête <algorithm> fournit des fonctions prêtes à l'emploi qui travaillent sur des plages d'éléments, délimitées par begin() et end() :

#include <algorithm>
#include <vector>

std::vector<int> temps{40, 10, 25};
std::sort(temps.begin(), temps.end());   // 10 25 40

auto it = std::find(temps.begin(), temps.end(), 25);
if (it != temps.end()) {
    std::cout << "Trouvé\n";
}

std::find renvoie un itérateur, objet qui désigne un élément. S'il vaut end(), rien n'a été trouvé.

Les lambdas

Un algorithme peut recevoir une fonction anonyme, la lambda, pour personnaliser son comportement :

std::vector<Recette> recettes{{"Tarte"}, {"Gratin"}, {"Soupe"}};

std::sort(recettes.begin(), recettes.end(),
          [](const Recette& a, const Recette& b) {
              return a.titre < b.titre;
          });

Les crochets [] précèdent les paramètres. Ils permettent aussi de capturer des variables locales : [seuil](int t) { return t < seuil; }.

std::map

Un std::map associe une clé à une valeur et garde ses clés triées :

#include <map>
#include <string>

std::map<std::string, int> notes;
notes["Gratin"] = 4;
notes["Tarte"] = 5;

for (const auto& [titre, note] : notes) {
    std::cout << titre << " : " << note << "\n";
}

La forme [titre, note] est une décomposition structurée, disponible depuis C++17. Pour tester la présence d'une clé sans la créer, utilise notes.find("Soupe") ou notes.count("Soupe").

L'écriture notes["Soupe"] crée l'entrée avec la valeur 0 si elle n'existe pas. Pour une simple lecture, préfère find ou at.

À retenir

  • Les variables locales vivent sur la pile et sont libérées toutes seules ; le tas sert aux objets à durée de vie plus longue.
  • Évite new et delete : un pointeur intelligent libère la mémoire pour toi.
  • std::unique_ptr a un seul propriétaire ; std::shared_ptr en autorise plusieurs.
  • Crée-les avec std::make_unique et std::make_shared.
  • std::sort et std::find travaillent sur une plage begin() à end(), avec une lambda pour personnaliser.
  • std::map associe clés et valeurs triées ; [] crée l'entrée si elle manque.

Pour aller plus loin

Sources officielles

Ensuite

Projet
Lignes de code
Niveau
Le quartier
Appartement
C++
Pièce
6/7
Plan n°
cpp/06.memoire-et-bibliotheque
Lignes de code · fiches rédigées à partir des documentations officielles, sources citées en bas de chaque page.