web-dev-qa-db-fra.com

std :: vector :: resize () vs. std :: vector :: reserve ()

Il y a un fil dans la section des commentaires dans cet article sur l'utilisation de std::vector::reserve() vs std::vector::resize().

Voici le code d'origine:

void MyClass::my_method()
{
    my_member.reserve(n_dim);
    for(int k = 0 ; k < n_dim ; k++ )
         my_member[k] = k ;
}

Je crois que pour écrire des éléments dans le vector, la bonne chose à faire est d'appeler std::vector::resize(), pas std::vector::reserve().

En fait, le code de test suivant "plante" dans les versions de débogage dans VS2010 SP1:

#include <vector>

using namespace std;

int main()
{
    vector<int> v;
    v.reserve(10);
    v[5] = 2;

    return 0;
}

Ai-je raison ou ai-je tort? Et VS2010 SP1 a-t-il raison, ou est-ce faux?

66
Mr.C64

Il existe deux méthodes différentes pour une raison:

std::vector::reserve allouera la mémoire mais ne redimensionnera pas votre vecteur, qui aura une taille logique identique à la précédente.

std::vector::resize modifiera réellement la taille de votre vecteur et remplira tout espace avec des objets dans leur état par défaut. S'ils sont des pouces, ils seront tous nuls.

Après réserve, dans votre cas, vous aurez besoin de beaucoup de Push_backs pour écrire dans l'élément 5. Si vous ne le souhaitez pas, vous devez utiliser resize dans votre cas.

97
CashCow

Répondu ici par Jan Hudec : Choix entre vector :: resize () et vector :: reserve ()

Les deux fonctions font des choses très différentes.

La méthode resize () (et passer un argument au constructeur équivaut à cela) insérera un nombre donné d'éléments dans le vecteur (elle a un deuxième argument facultatif pour spécifier leur valeur). Cela affectera la taille (), l'itération ira sur tous ces éléments, Push_back les insérera après et vous pourrez y accéder directement en utilisant l'opérateur [].

La méthode reserve () alloue uniquement de la mémoire, mais la laisse non initialisée. Cela n'affecte que la capacité (), mais la taille () restera inchangée. Il n'y a pas de valeur pour les objets, car rien n'est ajouté au vecteur. Si vous insérez ensuite les éléments, aucune réallocation ne se produira, car cela a été fait à l'avance, mais c'est le seul effet.

Cela dépend donc de ce que vous voulez. Si vous voulez un tableau de 1000 éléments par défaut, utilisez resize (). Si vous voulez un tableau dans lequel vous vous attendez à insérer 1000 éléments et que vous souhaitez éviter quelques allocations, utilisez reserve ().

EDIT: Le commentaire de Blastfurnace m'a fait relire la question et me rendre compte que, dans votre cas, la bonne réponse n'est pas de préallouer manuellement. Continuez à insérer les éléments à la fin selon vos besoins. Le vecteur se réallouera automatiquement selon les besoins et le fera plus efficacement que la manière manuelle mentionnée. Le seul cas où reserve () est logique est lorsque vous avez une estimation raisonnablement précise de la taille totale dont vous aurez besoin facilement disponible à l'avance.

EDIT2: Modification de la question de l'annonce: si vous avez une estimation initiale, alors réservez () cette estimation et si cela ne suffit pas, laissez simplement le vecteur faire son travail.

22
lucasg

Cela dépend de ce que vous voulez faire. reserve ne pas ajoute des éléments au vector; il modifie uniquement la capacity(), ce qui garantit que les éléments ajout ne seront pas réalloués (et par exemple invalideront les itérateurs). resize ajoute des éléments immédiatement. Si vous souhaitez ajouter des éléments ultérieurement (insert(), Push_back()), utilisez reserve. Si vous souhaitez accéder aux éléments ultérieurement (en utilisant [] Ou at()), utilisez resize. Vous pouvez donc MyClass::my_method Être soit:

void MyClass::my_method()
{
    my_member.clear();
    my_member.reserve( n_dim );
    for ( int k = 0; k < n_dim; ++ k ) {
        my_member.Push_back( k );
    }
}

ou

void MyClass::my_method()
{
    my_member.resize( n_dim );
    for ( int k = 0; k < n_dim; ++ k ) {
        my_member[k] = k;
    }
}

Lequel vous avez choisi est une question de goût, mais le code que vous citez est clairement incorrect.

11
James Kanze

Il devrait probablement y avoir une discussion sur le moment où les deux méthodes sont appelées avec un nombre inférieur à la taille actuelle du vecteur.

L'appel de reserve() avec un nombre inférieur à la capacité n'affectera pas la taille ou la capacité.

En appelant resize() avec un nombre inférieur à la taille actuelle, le conteneur sera réduit à cette taille, détruisant ainsi les éléments en excès.

Pour résumer, resize() libérera de la mémoire tandis que reserve() ne le fera pas.

2
Dula

Oui, vous avez raison, Luchian vient de faire une faute de frappe et est probablement trop privé de café pour réaliser son erreur.

2
Konrad Rudolph

le redimensionnement modifie en fait la quantité d'éléments dans le vecteur, de nouveaux éléments sont construits par défaut si le redimensionnement fait croître le vecteur.

vector<int> v;
v.resize(10);
auto size = v.size();

dans ce cas, la taille est de 10.

reserve d'autre part demande uniquement que le tampon interne soit agrandi à la taille spécifiée mais ne change pas la "taille" du tableau, seule sa taille de tampon est modifiée.

vector<int> v;
v.reserve(10);
auto size = v.size();

dans ce cas, la taille est toujours 0.

Donc, pour répondre à votre question, oui, vous avez raison, même si vous réservez suffisamment d'espace, vous accédez toujours à la mémoire non initialisée avec l'opérateur d'index. Avec un int c'est pas si mal mais dans le cas d'un vecteur de classes vous accéderez à des objets qui n'ont pas été construits.

La vérification des limites des compilateurs définis en mode débogage peut évidemment être confondue par ce comportement, ce qui peut être la raison pour laquelle vous rencontrez le crash.

1
odinthenerd