std :: vector :: resize () frente a std :: vector :: reserve ()

80

Hay un hilo en la sección de comentarios de este post sobre el uso std::vector::reserve()vs std::vector::resize().

Aquí está el código original:

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

Creo que para escribir elementos en el vector, lo correcto es llamar std::vector::resize(), no std::vector::reserve().

De hecho, el siguiente código de prueba "falla" en las compilaciones de depuración en VS2010 SP1:

#include <vector>

using namespace std;

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

    return 0;
}

¿Estoy en lo cierto o estoy equivocado? ¿VS2010 SP1 es correcto o está mal?

Señor C64
fuente
11
La explicación podría ser tan simple como "Me equivoqué": D
Luchian Grigore
7
Marqué esto como "demasiado localizado", ya que @LuchianGrigore rara vez se equivoca
Predeterminado el
1
@Default lee "rara vez se equivoca" como "rápido en corregir sus errores" :)
Luchian Grigore
1
El código de la publicación original se actualizó para usarlo correctamente resize()y se despejó la duda. Para los moderadores: no duden en eliminar esta pregunta si está "demasiado localizada" o conservarla si creen que podría ayudar a otra persona en el futuro.
Mr.C64
1
esta pregunta realmente aclara mis dudas cuando estoy migrando mi proyecto de vc6 a vs2013 .. gracias :))
pengMiao

Respuestas:

114

Hay dos métodos diferentes por una razón:

std::vector::reserve asignará la memoria, pero no cambiará el tamaño de su vector, que tendrá un tamaño lógico igual que antes.

std::vector::resizerealmente modificará el tamaño de su vector y llenará cualquier espacio con objetos en su estado predeterminado. Si son enteros, todos serán cero.

Después de reservar, en su caso, necesitará muchos push_backs para escribir en el elemento 5. Si no desea hacer eso, entonces en su caso debería usar resize.

Una cosa sobre la reserva: si luego agrega elementos con push_back, hasta que alcance la capacidad que ha reservado, las referencias, iteradores o punteros existentes a los datos en su vector seguirán siendo válidos. Entonces, si reservo 1000 y mi tamaño es 5, &vec[4]seguirá siendo el mismo hasta que el vector tenga 1000 elementos. Después de eso, puedo llamar push_back()y funcionará, pero es posible que el puntero almacenado de &vec[4]antes ya no sea válido.

CashCow
fuente
entonces, para el vector vacío, es decir, vec, después de la reserva vec [1] terminará con un fallo de segmento.
hailinzeng
2
vec [1] sería un comportamiento indefinido.
CashCow
¿ std::vector::reserveEvitará la copia ocasional de la matriz completa push_back?
Publicación propia el
¿Es esto solo para C ++ 11 o una implementación estándar específica? ¿Parece que el código con reserva y acceso con [] funciona bien? godbolt.org/z/MhgFdZ
Steve_Corrin
1
@Steve_Corrin El comportamiento indefinido no está definido. Puede parecer que funciona. Sigue siendo un código inválido. < size()No se permite la indexación en elementos que no existen dentro del contenedor. No existen allí, según la definición del idioma. Si su compilador decide no lanzar las armas nucleares y en lugar de eso, solo empuja / asoma la RAM de la manera que usted quiere, es buena suerte. O mala suerte, supongo; idealmente podríamos detectar todas las cosas inválidas que harían todos los programadores, pero ¡ buena suerte para llegar allí!
underscore_d
25

Respondido aquí por Jan Hudec : Elección entre vector :: resize () y vector :: reserve ()

Las dos funciones hacen cosas muy diferentes.

El método resize () (y pasar el argumento al constructor es equivalente a eso) insertará una cantidad determinada de elementos en el vector (tiene un segundo argumento opcional para especificar su valor). Afectará el tamaño (), la iteración pasará por todos esos elementos, push_back insertará después de ellos y puedes acceder directamente a ellos usando el operador [].

El método reserve () solo asigna memoria, pero la deja sin inicializar. Solo afecta la capacidad (), pero el tamaño () no 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 hizo con anticipación, 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 con anticipación.

EDIT2: Edición de la pregunta del anuncio: si tiene una estimación inicial, reserve () esa estimación y si resulta que no es suficiente, deje que el vector haga su trabajo.

lucasg
fuente
12

Depende de lo que quieras hacer. reserveno no añadir ningún elemento a la vector; solo cambia el capacity(), lo que garantiza que la adición de elementos no se reasignará (y, por ejemplo, invalidará los iteradores). resizeagrega elementos inmediatamente. Si desea agregar elementos más tarde ( insert(), push_back()), use reserve. Si desea acceder a los elementos más tarde (usando []o at()), use resize. Entonces MyClass::my_methodpuedes ser:

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 );
    }
}

o

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

Cuál eligió es una cuestión de gusto, pero el código que cita es claramente incorrecto.

James Kanze
fuente
3

Probablemente debería haber una discusión sobre cuándo se llaman a ambos métodos con un número MENOR que el tamaño actual del vector.

Llamar reserve()con un número menor que la capacidad no afectará el tamaño ni la capacidad.

Llamando resize()con un número menor que el tamaño actual, el contenedor se reducirá a ese tamaño destruyendo efectivamente el exceso de elementos.

En resumen resize(), liberará memoria mientras reserve()que no.

Dula
fuente
Cambiar el tamaño nunca libera memoria. Cuando el tamaño es menor, se llamarán destructores, pero la memoria se mantiene (la capacidad no cambia).
John Gordon
2

Sí, tienes razón, Luchian acaba de cometer un error tipográfico y probablemente esté demasiado privado de café para darse cuenta de su error.

Konrad Rudolph
fuente
1

redimensionar en realidad cambia la cantidad de elementos en el vector, los nuevos elementos se construyen por defecto si el redimensionamiento hace que el vector crezca.

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

en este caso el tamaño es 10.

reserve, por otro lado, solo solicita que el búfer interno crezca al tamaño especificado, pero no cambia el "tamaño" de la matriz, solo se cambia el tamaño del búfer.

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

en este caso, el tamaño sigue siendo 0.

Entonces, para responder a su pregunta, sí, tiene razón, incluso si reserva suficiente espacio, todavía está accediendo a la memoria no inicializada con el operador de índice. Con un int eso no es tan malo, pero en el caso de un vector de clases estaría accediendo a objetos que no han sido construidos.

La comprobación de límites de compiladores configurados en modo de depuración obviamente puede confundirse con este comportamiento, que puede ser la razón por la que está experimentando el bloqueo.

odinthenerd
fuente