web-dev-qa-db-fra.com

std :: vector versus std :: array en C ++

Quelle est la différence entre un std::vector et un std::array en C++? Quand faut-il préférer l'un à l'autre? Quels sont les avantages et les inconvénients de chacun? Tout ce que mon manuel fait est de lister en quoi ils sont identiques.

265
Zud

std::vector est une classe de modèle qui encapsule un tableau dynamique.1, stocké dans le tas, augmente et diminue automatiquement si des éléments sont ajoutés ou supprimés. Il fournit tous les points d'accroche (begin(), end(), itérateurs, etc.) qui permettent un fonctionnement optimal avec le reste de la STL. Il a également plusieurs méthodes utiles qui vous permettent d’effectuer des opérations qui seraient fastidieuses sur un tableau normal, comme par exemple. insérer des éléments au milieu d'un vecteur (il gère tout le travail de déplacement des éléments suivants dans les coulisses).

Dans la mesure où il stocke les éléments en mémoire alloués sur le segment de mémoire, il présente une surcharge en ce qui concerne les tableaux statiques.

std::array est une classe de modèle qui encapsule un tableau de taille statique, stocké à l'intérieur de l'objet même, ce qui signifie que si vous instanciez la classe sur la pile, le tableau lui-même sera sur la pile. Sa taille doit être connue au moment de la compilation (elle est passée en tant que paramètre de modèle) et elle ne peut ni croître ni se réduire.

Il est plus limité que std::vector, mais il est souvent plus efficace, en particulier pour les petites tailles, car dans la pratique, il s’agit essentiellement d’un wrapper léger autour d’un tableau de style C. Cependant, il est plus sécurisé, car la conversion implicite en pointeur est désactivée et fournit une grande partie des fonctionnalités de std::vector relatives à STL et des autres conteneurs. Vous pouvez donc l'utiliser facilement avec les algorithmes STL & co. Quoi qu'il en soit, pour la taille même de la taille fixe, il est beaucoup moins flexible que std::vector.

Pour une introduction à std::array, jetez un œil à cet article ; pour une brève introduction à std::vector et aux opérations possibles sur celui-ci, vous pouvez consulter sa documentation .


  1. En fait, je pense que dans la norme, ils sont décrits en termes de complexité maximale des différentes opérations (par exemple, accès aléatoire en temps constant, itération sur tous les éléments en temps linéaire, ajout et suppression d'éléments à la fin en temps amorti constant, etc.), mais autant que je sache, il n’ya pas d’autre méthode pour remplir de telles exigences que d’utiliser un tableau dynamique. Comme indiqué par @Lucretiel, la norme exige en fait que les éléments soient stockés de manière contiguë. Il s’agit donc d’un tableau dynamique, stocké à la place de l’allocateur associé.
298
Matteo Italia

Utilisation de la classe std::vector<T>:

  • ... est aussi rapide que l'utilisation de tableaux intégrés, en supposant que vous ne fassiez que ce que les tableaux intégrés vous permettent de faire (lire et écrire sur des éléments existants).

  • ... redimensionne automatiquement lorsque de nouveaux éléments sont insérés.

  • ... permet d'insérer de nouveaux éléments au début ou au centre du vecteur, en "décalant" automatiquement le reste des éléments "en haut" (est-ce que sens?). Il vous permet également de supprimer des éléments n’importe où dans le std::vector, en décalant automatiquement le reste des éléments vers le bas.

  • ... vous permet d'effectuer une lecture avec vérification de plage avec la méthode at() (vous pouvez toujours utiliser les indexeurs [] si vous ne voulez pas que cette vérification soit effectuée).

Il y a deux Trois mises en garde principales pour utiliser std::vector<T>:

  1. Vous n'avez pas d'accès fiable au pointeur sous-jacent, ce qui peut constitue un problème si vous utilisez des fonctions tierces qui demandent l'adresse d'un tableau.

  2. La classe std::vector<bool> est stupide. Il est implémenté comme un champ de bits condensé, pas comme un tableau. Evitez-le si vous voulez un tableau de bools!

  3. Pendant l'utilisation, std::vector<T>s sera un peu plus grand qu'un tableau C++ avec le même nombre d'éléments. En effet, ils doivent garder trace d'une petite quantité d'informations, telles que leur taille actuelle, et parce que chaque fois que std::vector<T>s redimensionnent, ils réservent plus d'espace que nécessaire. Cela leur évitera d'avoir à redimensionner chaque fois qu'un nouvel élément est inséré. Ce comportement peut être modifié en fournissant un paramètre personnalisé allocator, mais je n’ai jamais ressenti le besoin de le faire!


Edit: Après avoir lu la réponse de Zud à la question, j’ai pensé que je devrais ajouter ceci:

La classe std::array<T> n'est pas identique à un tableau C++. std::array<T> est une enveloppe très fine entourant les tableaux C++, dans le but principal de masquer le pointeur à l'utilisateur de la classe (en C++, les tableaux sont implicitement convertis en pointeurs, souvent pour en démolir les effets). La classe std::array<T> stocke également sa taille (longueur), ce qui peut être très utile.

15
ClosureCowboy

Pour souligner un point soulevé par @MatteoItalia, la différence d'efficacité réside dans le lieu où les données sont stockées. La mémoire de tas (requise avec vector) nécessite un appel au système pour allouer de la mémoire, ce qui peut être coûteux si vous comptez des cycles. La mémoire de pile (possible pour array) correspond pratiquement à un "surcoût" en termes de temps, car la mémoire est allouée en ajustant simplement le pointeur de pile et elle est effectuée une seule fois lors de l'entrée d'une fonction. La pile évite également la fragmentation de la mémoire. Pour être sûr, std::array ne sera pas toujours sur la pile; cela dépend de l'endroit où vous l'allouez, mais cela impliquera tout de même une allocation de mémoire de moins du tas par rapport au vecteur. Si tu as un

  • petit "tableau" (moins de 100 éléments, par exemple) - (une pile typique fait environ 8 Mo, alors n'allouez pas plus de quelques Ko sur la pile ou moins si votre code est récursif)
  • la taille sera fixée
  • la durée de vie est dans l'étendue de la fonction (ou est une valeur de membre ayant la même durée de vie que la classe parente)
  • vous comptez les cycles,

utiliser définitivement un std::array sur un vecteur. Si l'une de ces exigences n'est pas vraie, utilisez un std::vector.

14
Mark Lakata

Si vous envisagez d'utiliser des tableaux multidimensionnels, il existe une différence supplémentaire entre std :: array et std :: vector. Un std :: array multidimensionnel aura les éléments empaquetés en mémoire dans toutes les dimensions, tout comme un tableau de style c. Un std :: vector multidimensionnel ne sera pas compressé dans toutes les dimensions.

Compte tenu des déclarations suivantes:

int cConc[3][5];
std::array<std::array<int, 5>, 3> aConc;
int **ptrConc;      // initialized to [3][5] via new and destructed via delete
std::vector<std::vector<int>> vConc;    // initialized to [3][5]

Un pointeur sur le premier élément du tableau de style c (cConc) ou de std :: array (aConc) peut être itéré dans tout le tableau en ajoutant 1 à chaque élément précédent. Ils sont bien emballés.

Un pointeur sur le premier élément du tableau de vecteurs (vConc) ou le tableau de pointeurs (ptrConc) ne peut être itéré que par les 5 premiers éléments (dans ce cas), puis il y a 12 octets (sur mon système) de surcharge pour le vecteur suivant.

Cela signifie qu'un tableau std :: vector> initialisé en tant que tableau [3] [1000] sera beaucoup plus petit en mémoire qu'un tableau initialisé en tant que tableau [1000] [3], et les deux seront plus grands en mémoire qu'un tableau std: tableau alloué de toute façon.

Cela signifie également que vous ne pouvez pas simplement passer un tableau de vecteur (ou de pointeur) multidimensionnel à, par exemple, openGL sans prendre en compte la surcharge de mémoire, mais vous pouvez passer naïvement un std :: array multidimensionnel à openGL et le faire fonctionner.

11
psimpson