J'aime la réponse de Matthieu, mais je vais reformuler l'organigramme comme suit:
Quand NE PAS utiliser std :: vector
Par défaut, si vous avez besoin d'un conteneur de trucs, utilisez std::vector
. Ainsi, tout autre conteneur n'est justifié qu'en fournissant une fonctionnalité alternative à std::vector
.
Constructeurs
std::vector
exige que son contenu soit constructible par déplacement, car il doit être capable de mélanger les éléments. Ce n'est pas un fardeau terrible à placer sur le contenu (notez que les constructeurs par défaut ne sont pas nécessaires , grâce à emplace
et ainsi de suite). Cependant, la plupart des autres conteneurs ne nécessitent aucun constructeur particulier (encore une fois, grâce à emplace
). Donc, si vous avez un objet pour lequel vous ne pouvez absolument pas implémenter un constructeur de déplacement, vous devrez choisir autre chose.
Un std::deque
serait le remplacement général, ayant de nombreuses propriétés de std::vector
, mais vous ne pouvez insérer qu'aux deux extrémités du deque. Les inserts au milieu nécessitent un déplacement. A std::list
n'impose aucune exigence sur son contenu.
Besoin de booléens
std::vector<bool>
n'est pas. Eh bien, c'est standard. Mais ce n'est pas un vector
dans le sens habituel, car les opérations qui le std::vector
permettent normalement sont interdites. Et il ne contientbool
certainement pas l' art .
Par conséquent, si vous avez besoin d'un vector
comportement réel d'un conteneur de bool
s, vous n'allez pas l'obtenir std::vector<bool>
. Vous devrez donc respecter un std::deque<bool>
.
Recherche
Si vous avez besoin de trouver des éléments dans un conteneur et que la balise de recherche ne peut pas être simplement un index, vous devrez peut-être abandonner std::vector
en faveur de set
et map
. Notez le mot clé « peut »; un tri std::vector
est parfois une alternative raisonnable. Ou Boost.Container's flat_set/map
, qui implémente un fichier trié std::vector
.
Il existe désormais quatre variantes de celles-ci, chacune ayant ses propres besoins.
- Utilisez a
map
lorsque la balise de recherche n'est pas la même chose que l'élément que vous recherchez. Sinon, utilisez un set
.
- À utiliser
unordered
lorsque vous avez beaucoup d'éléments dans le conteneur et que les performances de recherche doivent absolument l'être O(1)
plutôt que O(logn)
.
- À utiliser
multi
si vous avez besoin que plusieurs éléments aient la même balise de recherche.
Commande
Si vous avez besoin qu'un conteneur d'éléments soit toujours trié en fonction d'une opération de comparaison particulière, vous pouvez utiliser un fichier set
. Ou multi_set
si vous avez besoin de plusieurs éléments pour avoir la même valeur.
Ou vous pouvez utiliser un trié std::vector
, mais vous devrez le garder trié.
Stabilité
L'invalidation des itérateurs et des références pose parfois problème. Si vous avez besoin d'une liste d'éléments, de telle sorte que vous ayez des itérateurs / pointeurs vers ces éléments à divers autres endroits, alors std::vector
l'approche de l'invalidation peut ne pas être appropriée. Toute opération d'insertion peut entraîner une invalidation, selon la taille et la capacité actuelles.
std::list
offre une garantie ferme: un itérateur et ses références / pointeurs associés ne sont invalidés que lorsque l'élément lui-même est retiré du conteneur. std::forward_list
est là si la mémoire est un problème sérieux.
Si c'est une garantie trop forte, std::deque
offre une garantie plus faible mais utile. L'invalidation résulte d'insertions au milieu, mais les insertions en tête ou en queue ne provoquent que l'invalidation des itérateurs , pas des pointeurs / références aux éléments dans le conteneur.
Performances d'insertion
std::vector
ne fournit qu'une insertion bon marché à la fin (et même dans ce cas, cela devient coûteux si vous soufflez de capacité).
std::list
est cher en termes de performances (chaque élément nouvellement inséré coûte une allocation de mémoire), mais il est cohérent . Il offre également la capacité parfois indispensable de mélanger des articles pour pratiquement aucun coût de performance, ainsi que d'échanger des articles avec d'autres std::list
conteneurs du même type sans perte de performance. Si vous devez beaucoup mélanger les choses , utilisez std::list
.
std::deque
fournit une insertion / retrait en temps constant à la tête et à la queue, mais l'insertion au milieu peut être assez coûteuse. Donc, si vous avez besoin d'ajouter / supprimer des éléments de l'avant comme de l'arrière, c'est std::deque
peut-être ce dont vous avez besoin.
Il convient de noter que, grâce à la sémantique de déplacement, std::vector
les performances d'insertion peuvent ne pas être aussi mauvaises qu'auparavant. Certaines implémentations ont implémenté une forme de copie d'élément basée sur la sémantique (la soi-disant "swaptimisation"), mais maintenant que le déplacement fait partie du langage, il est mandaté par le standard.
Aucune allocation dynamique
std::array
est un bon conteneur si vous voulez le moins d'allocations dynamiques possibles. C'est juste un wrapper autour d'un C-array; cela signifie que sa taille doit être connue au moment de la compilation . Si vous pouvez vivre avec cela, utilisez std::array
.
Cela étant dit, utiliser std::vector
et utiliser reserve
une taille fonctionnerait tout aussi bien pour un borné std::vector
. De cette façon, la taille réelle peut varier et vous n'obtenez qu'une seule allocation de mémoire (sauf si vous faites exploser la capacité).