Elección entre vector :: resize () y vector :: reserve ()

151

Estoy preasignando algo de memoria a mi vectorvariable miembro. El siguiente código es parte mínima

class A {
  vector<string> t_Names;
public:
  A () : t_Names(1000) {}
};

Ahora en algún momento, si el t_Names.size()igual 1000. Tengo la intención de aumentar el tamaño en 100. Luego, si llega 1100, nuevamente aumenta en 100y así sucesivamente.

Mi pregunta es, qué elegir entre vector::resize()y vector::reserve(). ¿Hay alguna mejor opción en este tipo de escenario?

Editar : Tengo una especie de estimación precisa para el t_Names. Calculo que sea en torno 700a 800. Sin embargo, en ciertas situaciones (rara vez), puede crecer más que 1000.

iammilind
fuente
34
Te das cuenta de que hacer esto significa que el crecimiento del vector ya no se amortiza a tiempo constante y pierdes uno de los beneficios de rendimiento del uso std::vector.
Alto horno
1
Relacionado, vea C ++ más fácil: cómo crecen los vectores en el sitio del Dr. Dobbs.
jww

Respuestas:

262

¡Las dos funciones hacen cosas muy diferentes!

El resize()método (y pasar el argumento al constructor es equivalente a eso) insertará o eliminará el número apropiado de elementos al vector para darle un tamaño dado (tiene un segundo argumento opcional para especificar su valor). Afectará a size(), la iteración repasará todos esos elementos, push_back se insertará después de ellos y puede acceder directamente a ellos usando operator[].

El reserve()método solo asigna memoria, pero la deja sin inicializar. Solo afecta capacity(), pero no size()cambiará. No hay valor para los objetos, porque no se agrega nada al vector. Si luego inserta los elementos, no ocurrirá ninguna reasignación, porque se realizó de antemano, pero ese es el único efecto.

Entonces depende de lo que quieras. Si desea una matriz de 1000 elementos predeterminados, use resize(). Si desea una matriz en la que espera insertar 1000 elementos y desea evitar un par de asignaciones, use reserve().

EDITAR: El comentario de Blastfurnace me hizo leer la pregunta nuevamente y darme cuenta de que, en su caso, la respuesta correcta es no preasignar manualmente. Simplemente siga insertando los elementos al final según lo necesite. El vector se reasignará automáticamente según sea necesario y lo hará de manera más eficiente que la forma manual mencionada. El único caso en el que reserve()tiene sentido es cuando tiene una estimación razonablemente precisa del tamaño total que necesitará fácilmente disponible por adelantado.

EDIT2: Edición de pregunta publicitaria: si tiene una estimación inicial, entonces reserve()esa estimación. Si resulta que no es suficiente, simplemente deja que el vector haga lo suyo.

Jan Hudec
fuente
He editado la pregunta. Tengo cierta estimación para el vector.
iammilind
3
@ Jan: bueno, es frágil o no según lo difícil que haya sido para usted mantener la propiedad requerida. Algo así x.reserve(x.size() + newdata); vector<int>::iterator special_element = get_special_element(x); for (int i = 0; i < newdata; ++i) { if some_function(i, special_element) x.push_back(i); }es bastante robusto en lo que respecta a reservar el espacio. No tengo idea de cuántos elementos se agregarán realmente, pero tengo un límite superior. Por supuesto, cuando tenga dudas, con los vectores solo puede usar índices en lugar de iteradores, la diferencia generalmente es insignificante.
Steve Jessop
44
Su redacción tiene sentido para alguien que ya conoce la respuesta correcta, pero podría confundir fácilmente a las personas que necesitan hacer la pregunta. "resize () ... insertará un número dado de elementos en el vector", solo cierto la primera vez que se usa, generalmente inserta la diferencia entre el número solicitado y el preexistente size(). "El método reserve () solo asigna memoria" - puede o no asignar memoria dependiendo de si capacity()ya es suficiente, también puede necesitar mover elementos y desasignar su memoria original. "quiere evitar un par de asignaciones" y copias, etc.
Tony Delroy
19
En realidad, reservar antes de empujar es vital y debe usarse. Suponga que está codificando algún tipo de cargador de modelos 3D y que el modelo tiene unos 15000 vértices. Si intenta hacer retroceder cada vértice mientras se carga sin asignarlos previamente, tomará mucho tiempo. Personalmente, experimenté eso, intenté cargar un modelo de automóvil .obj con cerca de 100000 vértices, tardó 30 segundos. Luego refactoré el código usando la preasignación con .reserve (), ahora toma 3 segundos. Solo poner un .reserve (100000) al comienzo del código ahorró 27 segundos.
deniz
1
@deniz Eso es trivial cierto en la escala 100000, pero no es cierto en la escala 100-300, donde reservar puede ser un desperdicio si se hace innecesariamente.
deworde
30

resize()no solo asigna memoria, sino que también crea tantas instancias como el tamaño deseado que usted pasa resize()como argumento. Pero reserve()solo asigna memoria, no crea instancias. Es decir,

std::vector<int> v1;
v1.resize(1000); //allocation + instance creation
cout <<(v1.size() == 1000)<< endl;   //prints 1
cout <<(v1.capacity()==1000)<< endl; //prints 1

std::vector<int> v2;
v2.reserve(1000); //only allocation
cout <<(v2.size() == 1000)<< endl;   //prints 0
cout <<(v2.capacity()==1000)<< endl; //prints 1

Salida ( demostración en línea ):

1
1
0
1

Por resize()lo tanto, puede no ser deseable, si no desea los objetos creados por defecto. Será lento también. Además, si le push_back()agrega nuevos elementos, la size()del vector aumentará aún más asignando nueva memoria (lo que también significa mover los elementos existentes al espacio de memoria recién asignado). Si lo ha utilizado reserve()al principio para asegurarse de que ya hay suficiente memoria asignada, la size()del vector aumentará cuando lo haga push_back(), pero no asignará nueva memoria nuevamente hasta que se agote el espacio que le reservó .

Nawaz
fuente
66
Después de hacerlo reserve(N), podemos usarlo sin operator []causar daño. correcto?
iammilind
2
Si bien la mayoría de las implementaciones asignarán la cantidad exacta por la que solicita reserve, la especificación solo requiere que asigne al menos esa cantidad, por lo que algunas implementaciones pueden redondearse a algún límite y, por lo tanto, mostrar una capacidad superior a 1000.
Jan Hudec,
16
@iammilind: No, si el índice es mayor o igual a v.size(). Tenga en cuenta que reserve(N)no cambia size()de vector.
Nawaz
55
@iammilind: Incorrecto. Después de llamar a reSERVE, no se agregan entradas, solo se obtiene suficiente memoria para agregarlas.
Jan Hudec
2

Según su descripción, parece que desea "reservar" el espacio de almacenamiento asignado del vector t_Names.

Tenga en cuenta que resizeinicializa el vector recién asignado donde reservesolo asigna pero no construye. Por lo tanto, 'reservar' es mucho más rápido que 'cambiar tamaño'

Puede consultar la documentación sobre la diferencia de cambio de tamaño y reserva

inmersión
fuente
1
Consulte aquí en su lugar: vector y capacidad ( ¿por qué? )
Sehe
1
Gracias por agregar el enlace, sehe
dip
2

reserve cuando no desee que los objetos se inicialicen cuando esté reservado. Además, es posible que prefiera diferenciar lógicamente y realizar un seguimiento de su recuento frente a su recuento de uso cuando cambie el tamaño. por lo tanto, existe una diferencia de comportamiento en la interfaz: el vector representará la misma cantidad de elementos cuando esté reservado, y será 100 elementos más grande cuando se redimensione en su escenario.

¿Hay alguna mejor opción en este tipo de escenario?

depende completamente de tus objetivos cuando luches contra el comportamiento predeterminado. algunas personas favorecerán asignadores personalizados, pero realmente necesitamos una mejor idea de lo que está intentando resolver en su programa para asesorarlo bien.

fwiw, muchas implementaciones de vectores simplemente duplicarán el recuento de elementos asignados cuando deben crecer: ¿está tratando de minimizar los tamaños máximos de asignación o está tratando de reservar suficiente espacio para algún programa sin bloqueo u otra cosa?

justin
fuente
" reserve cuando no desee que los objetos se inicialicen cuando esté reservado " . La formulación correcta es cuando no desea que existan los objetos . No es como una matriz no inicializada de un tipo trivialmente construible, donde los objetos no se pueden leer pero se les puede asignar; más bien, solo la memoria está reservada, pero no existen objetos en ella, por lo que no se puede acceder a ellos operator[]ni nada.
underscore_d