web-dev-qa-db-fra.com

Capacité initiale du vecteur en C++

Quel est le capacity() d'un std::vector créé à l'aide du constituant par défaut? Je sais que size() est zéro. Pouvons-nous affirmer qu'un vecteur construit par défaut n'appelle pas l'allocation de mémoire en tas?

De cette façon, il serait possible de créer un tableau avec une réserve arbitraire en utilisant une seule allocation, telle que std::vector<int> iv; iv.reserve(2345);. Disons que pour une raison quelconque, je ne veux pas commencer le size() à 2345.

Par exemple, sous Linux (g ++ 4.4.5, noyau 2.6.32 AMD64)

#include <iostream>
#include <vector>

int main()
{
  using namespace std;
  cout << vector<int>().capacity() << "," << vector<int>(10).capacity() << endl;
  return 0;
}

imprimé 0,10. Est-ce une règle ou dépend-il du fournisseur STL?

68
Notinlist

La norme ne spécifie pas ce que la capacity initiale d'un conteneur devrait être, vous vous fiez donc à la mise en œuvre. Une mise en œuvre commune démarrera la capacité à zéro, mais il n'y a aucune garantie. En revanche, il n’ya aucun moyen d’améliorer votre stratégie de std::vector<int> iv; iv.reserve(2345);, alors tenez-vous-en à cela.

53
Mark Ransom

Les implémentations de stockage de std :: vector varient de manière significative, mais toutes celles que j'ai rencontrées commencent à 0.

Le code suivant:

#include <iostream>
#include <vector>

int main()
{
  using namespace std;

  vector<int> normal;
  cout << normal.capacity() << endl;

  for (unsigned int loop = 0; loop != 10; ++loop)
  {
      normal.Push_back(1);
      cout << normal.capacity() << endl;
  }

  std::cin.get();
  return 0;
}

Donne le résultat suivant:

0
1
2
4
4
8
8
8
8
16
16

sous GCC 5.1 et:

0
1
2
3
4
6
6
9
9
9
13

sous MSVC 2013.

20
metamorphosis

Pour compléter légèrement les autres réponses, j’ai constaté que lorsqu’il est exécuté dans des conditions de débogage avec Visual Studio, un vecteur construit par défaut reste alloué sur le segment de mémoire même si sa capacité commence à zéro.

Spécifiquement si _ITERATOR_DEBUG_LEVEL! = 0 alors vector, allouera de l’espace pour faciliter la vérification des itérateurs.

https://docs.Microsoft.com/en-gb/cpp/standard-library/iterator-debug-level

Je viens de trouver cela légèrement gênant puisque j’utilisais un allocateur personnalisé à l’époque et que je n’attendais pas l’allocation supplémentaire.

3
David Woo

Pour autant que j'ai compris la norme (bien que je ne puisse pas réellement nommer de référence), l'instanciation de conteneur et l'allocation de mémoire ont été délibérément découplées pour une bonne raison. Par conséquent, vous avez des appels distincts, séparés pour

  • constructor pour créer le conteneur lui-même
  • reserve() pour allouer préalablement un bloc de mémoire suffisamment volumineux pour accueillir au moins (!) un nombre donné d'objets

Et cela a beaucoup de sens. Le seul droit à exister pour reserve() est de vous donner la possibilité de coder des réallocations éventuellement coûteuses lors de la croissance du vecteur. Pour être utile, vous devez connaître le nombre d'objets à stocker ou au moins être capable de faire une supposition éclairée. Si cela ne vous est pas donné, évitez plutôt reserve() car vous ne ferez que modifier la réallocation pour le gaspillage de mémoire.

Donc, tout mettre ensemble:

  • La norme spécifie intentionnellementnotun constructeur qui vous permet de préallouer un bloc de mémoire pour un nombre spécifique d’objets (ce qui serait au moins plus souhaitable que l’attribution d’un "quelque chose" spécifique à une implémentation. la hotte).
  • L'allocation ne devrait pas être implicite. Donc, pour préallouer un bloc, vous devez faire un appel séparé à reserve() et ce dernier n’a pas besoin de se trouver au même endroit de construction (peut/devrait bien sûr être plus tard, une fois que vous avez pris connaissance de la taille requise pour accueillir)
  • Ainsi, si un vecteur préalloue toujours un bloc de mémoire de taille définie par la mise en œuvre, cela déjouera le travail prévu de reserve(), n'est-ce pas?
  • Quel serait l’avantage de préallouer un bloc si le TSL ne peut naturellement pas connaître le but recherché et la taille attendue d’un vecteur? Ce sera plutôt absurde, voire contre-productif.
  • La solution appropriée consiste plutôt à allouer et à mettre en œuvre un bloc spécifique avec le premier Push_back() - s'il n'a pas déjà été explicitement alloué auparavant par reserve().
  • En cas de réaffectation nécessaire, l'augmentation de la taille du bloc est également spécifique à la mise en œuvre. Les implémentations vectorielles que je connais commencent par une augmentation exponentielle de la taille mais plafonnent le taux d’incrément à un certain maximum pour éviter de gaspiller d’énormes quantités de mémoire ou même de les gaspiller.

Tout cela ne fonctionne pleinement et n’est avantageux que s’il n’est pas perturbé par un constructeur qui alloue. Vous avez des valeurs par défaut raisonnables pour les scénarios courants qui peuvent être remplacés à la demande par reserve() (et shrink_to_fit()). Donc, même si le standard ne le dit pas explicitement, je suis tout à fait sûr de supposer qu'un vecteur nouvellement construit ne préalloue pas est un pari relativement sûr pour toutes les implémentations actuelles.

3
Don Pedro

Standard ne spécifie pas la valeur initiale de la capacité, mais le conteneur STL s'adapte automatiquement au nombre de données que vous avez inséré, à condition de ne pas dépasser la taille maximale (utilisez la fonction membre max_size à connaître) . est gérée par realloc chaque fois que plus d'espace est requis. Supposons que vous souhaitiez créer un vecteur contenant une valeur de 1 000 à 1 000. Sans utiliser de réserve, le code entraînera généralement entre 2 et 18 réallocations lors de la boucle suivante:

vector<int> v;
for ( int i = 1; i <= 1000; i++) v.Push_back(i);

Modifier le code pour utiliser la réserve peut entraîner 0 affectation pendant la boucle:

vector<int> v;
v.reserve(1000);

for ( int i = 1; i <= 1000; i++) v.Push_back(i);

En gros, les capacités de vecteur et de chaîne sont multipliées par un facteur compris entre 1,5 et 2 à chaque fois.

0
Archie Yalakki