Supprimer un élément d'un vecteur par index : anatomie de std::vector::erase

Formateur Python en ligne pour débutants

Apprenez Python facilement sans surcharge théorique. Résolvez des tâches pratiques avec vérification automatique et écrivez du code directement dans le navigateur.

Start Course

Comment effacer un élément de std::vector par index : plongée sous le capot

\n\n

Salut, collègue ! Si tu as déjà codé en C++ et manipulé des tableaux dynamiques, tu t'es sûrement posé la question : "How do I erase an element from std::vector<> by index?" — c'est l'une des requêtes les plus fréquentes sur Stack Overflow, et pour cause. À première vue, la tâche semble triviale : tu appelles erase et le tour est joué. Mais que se passe-t-il réellement en mémoire ? Pourquoi la suppression au milieu d'un vecteur coûte-t-elle cher ? Et comment éviter les pièges avec les itérateurs ?

\n\n

Dans cet article, nous ne nous contenterons pas de montrer comment écrire v.erase(v.begin() + index). Nous allons comprendre comment cette opération fonctionne sous le capot, quelles structures de données sont impliquées et comment choisir la méthode de suppression optimale. C'est parti !

\n\n

1. std::vector : de quoi s'agit-il ?

\n\n

Avant de supprimer, rappelons comment std::vector est conçu. C'est un tableau dynamique qui stocke les éléments dans une zone mémoire contiguë. Il possède trois pointeurs (ou itérateurs) clés :

\n
    \n
  • begin — le début de la mémoire allouée ;
  • \n
  • end — la fin de la mémoire utilisée (après le dernier élément) ;
  • \n
  • capacity — la fin de la mémoire allouée (limite jusqu'à laquelle on peut grandir sans réallocation).
  • \n
\n\n

Visuellement, cela ressemble à ceci :

\n
[0] [1] [2] [3] [4] ... [capacity-1]\n ^                        ^\n begin                   end\n
\n\n

Lorsque tu supprimes un élément au milieu, le vecteur ne se contente pas de le "jeter" — il décale physiquement tous les éléments suivants vers la gauche. C'est là le principal "point douloureux" de l'opération erase.

\n\n

2. Suppression par index : syntaxe de base

\n\n

La façon la plus simple de supprimer un élément par index est d'utiliser la méthode erase avec un itérateur :

\n\n
#include <vector>\n#include <iostream>\n\nint main() {\n    std::vector<int> v = {10, 20, 30, 40, 50};\n    int index = 2; // on veut supprimer le troisième élément (30)\n\n    // Vérification que l'index est dans les limites\n    if (index < v.size()) {\n        v.erase(v.begin() + index);\n    }\n\n    for (int x : v) std::cout << x << " ";\n    // Affichage : 10 20 40 50\n    return 0;\n}\n
\n\n

Simple, non ? Mais que faire si tu dois supprimer plusieurs éléments consécutifs ? Il existe une surcharge erase(first, last) :

\n\n
v.erase(v.begin() + 1, v.begin() + 3); // supprime les éléments aux indices 1 et 2\n
\n\n

3. Et sous le capot ? Analyse détaillée

\n\n

Passons maintenant à la partie intéressante. Quand tu appelles v.erase(iterator), voici ce qui se passe en interne :

\n\n
    \n
  1. Vérification de l'itérateur (en mode debug) — on s'assure qu'il est valide et pointe sur un élément du vecteur.
  2. \n
  3. Décalage des éléments — tous les éléments situés après l'élément supprimé sont copiés (ou déplacés, si le type supporte la sémantique de mouvement) d'une position vers la gauche.
  4. \n
  5. Réduction de la taille — le compteur interne size est diminué de 1.
  6. \n
  7. Destructeur — pour l'élément "de queue" (qui est maintenant dupliqué), le destructeur est appelé (si le type n'est pas trivial).
  8. \n
\n\n

Regardons une implémentation simplifiée (pseudo-code) :

\n\n
template<typename T>\ntypename std::vector<T>::iterator\nvector<T>::erase(iterator pos) {\n    // 1. Décaler tous les éléments après pos d'une position vers la gauche\n    for (auto it = pos; it != end() - 1; ++it) {\n        *it = std::move(*(it + 1));\n    }\n    // 2. Réduire la taille\n    --size;\n    // 3. Appeler le destructeur pour le dernier élément\n    allocator.destroy(end()); // end() pointe maintenant sur l'ancien dernier élément\n    return pos; // retourne un itérateur sur l'élément suivant celui supprimé\n}\n
\n\n

Remarque l'utilisation de std::move — le C++ moderne essaie d'éviter les copies inutiles. Cette optimisation est cruciale pour les types coûteux à copier.

Blogs

Book Recommendations