Mémoire, pointeurs intelligents et bibliothèque standard
Ce que tu vas apprendre
- La différence entre la pile et le tas.
- Posséder un objet avec
std::unique_ptroustd::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.
| Pile | Tas | |
|---|---|---|
| Libération | Automatique, en fin de bloc | Manuelle ou via un pointeur intelligent |
| Taille | Limitée | Grande |
| Usage | Variables locales | Objets à 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
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").
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
newetdelete: un pointeur intelligent libère la mémoire pour toi. std::unique_ptra un seul propriétaire ;std::shared_ptren autorise plusieurs.- Crée-les avec
std::make_uniqueetstd::make_shared. std::sortetstd::findtravaillent sur une plagebegin()àend(), avec une lambda pour personnaliser.std::mapassocie 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