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

283

¿Cuál es la diferencia entre a std::vectory an std::arrayen C ++? ¿Cuándo se debe preferir uno sobre otro? ¿Cuáles son los pros y los contras de cada uno? Todo lo que mi libro de texto hace es enumerar cómo son iguales.

Zud
fuente
1
Estoy buscando una comparación de std::vectorvs. std::arrayy cómo los términos son diferentes.
Zud
Zud, std::arrayno es lo mismo que una matriz C ++. std::arrayes un contenedor muy delgado alrededor de las matrices de C ++, con el objetivo principal de ocultar el puntero al usuario de la clase. Actualizaré mi respuesta.
ClosureCowboy
Actualicé el título y el texto de la pregunta para reflejar su aclaración.
Matteo Italia

Respuestas:

318

std::vectores una clase de plantilla que encapsula una matriz dinámica 1 , almacenada en el montón, que crece y se reduce automáticamente si se agregan o eliminan elementos. Proporciona todos los ganchos ( begin(), end()iteradores, etc.) que hacen que funcione bien con el resto del STL. También tiene varios métodos útiles que le permiten realizar operaciones que en una matriz normal serían engorrosas, como, por ejemplo, insertar elementos en el medio de un vector (maneja todo el trabajo de mover los siguientes elementos detrás de escena).

Como almacena los elementos en la memoria asignada en el montón, tiene algo de sobrecarga con respecto a las matrices estáticas.

std::arrayes una clase de plantilla que encapsula una matriz de tamaño estático, almacenada dentro del propio objeto, lo que significa que, si crea una instancia de la clase en la pila, la matriz misma estará en la pila. Su tamaño debe conocerse en el momento de la compilación (se pasa como un parámetro de plantilla) y no puede crecer ni reducirse.

Es más limitado que std::vector, pero a menudo es más eficiente, especialmente para tamaños pequeños, porque en la práctica es principalmente un envoltorio liviano alrededor de una matriz de estilo C. Sin embargo, es más seguro, ya que la conversión implícita a puntero está deshabilitada, y proporciona gran parte de la funcionalidad relacionada con STL de std::vectory de los otros contenedores, por lo que puede usarla fácilmente con algoritmos STL & co. De todos modos, por la limitación del tamaño fijo, es mucho menos flexible que std::vector.

Para una introducción a std::array, eche un vistazo a este artículo ; para una introducción rápida a std::vectorlas operaciones que son posibles en él, puede consultar su documentación .


  1. En realidad, creo que en el estándar se describen en términos de máxima complejidad de las diferentes operaciones (por ejemplo, acceso aleatorio en tiempo constante, iteración sobre todos los elementos en tiempo lineal, adición y eliminación de elementos al final en tiempo amortizado constante, etc.), pero AFAIK no hay otro método para cumplir tales requisitos que no sea el uso de una matriz dinámica. Según lo indicado por @Lucretiel, el estándar realmente requiere que los elementos se almacenen contiguamente, por lo que es una matriz dinámica, almacenada donde el asignador asociado lo coloca.
Matteo Italia
fuente
66
Con respecto a su nota al pie: Si bien es cierto, el estándar también garantiza que la aritmética del puntero en los elementos internos funcione, lo que significa que tiene que ser una matriz: & vec [9] - & vec [3] == 6 es verdadero.
Lucretiel
9
Estoy bastante seguro de que ese vector no se contrae automáticamente, pero desde C ++ 11 puede llamar a shrink_to_fit.
Dino
Estoy totalmente confundido por el término matriz estática y no estoy seguro de cuál es la terminología correcta. Se refiere a una matriz de tamaño estático y no a una matriz variable estática (una que usa almacenamiento estático). stackoverflow.com/questions/2672085/… . ¿Cuál es la terminología correcta? ¿Es una matriz estática un término descuidado para una matriz con un tamaño fijo?
Z boson
3
@Zboson: definitivamente no eres solo tú, estático es un término bastante abusado; la misma staticpalabra clave en C ++ tiene tres significados diferentes no relacionados, y el término también se usa a menudo para hablar de cosas que se corrigen en tiempo de compilación. Espero que el "tamaño estático" sea un poco más claro.
Matteo Italia
3
Una cosa a tener en cuenta: para la programación en tiempo real (donde no se supone que tenga ninguna asignación / desasignación dinámica después del inicio), probablemente se preferiría std :: array sobre std :: vector.
TED
17

Usando la std::vector<T>clase:

  • ... es tan rápido como usar matrices integradas, suponiendo que solo está haciendo lo que las matrices integradas le permiten hacer (leer y escribir en elementos existentes).

  • ... cambia automáticamente de tamaño cuando se insertan nuevos elementos.

  • ... le permite insertar nuevos elementos al principio o en el medio del vector, "desplazando" automáticamente el resto de los elementos "hacia arriba" (¿tiene sentido?). También le permite eliminar elementos en cualquier parte del std::vector, desplazando automáticamente el resto de los elementos hacia abajo.

  • ... le permite realizar una lectura de verificación de rango con el at()método (siempre puede usar los indexadores []si no desea que se realice esta verificación).

Hay dos tres advertencias principales para usar std::vector<T>:

  1. No tiene acceso confiable al puntero subyacente, lo que puede ser un problema si se trata de funciones de terceros que exigen la dirección de una matriz.

  2. La std::vector<bool>clase es tonta. Se implementa como un campo de bits condensado, no como una matriz. ¡Evítalo si quieres una variedad de bools!

  3. Durante el uso, los std::vector<T>s serán un poco más grandes que una matriz C ++ con el mismo número de elementos. Esto se debe a que necesitan realizar un seguimiento de una pequeña cantidad de otra información, como su tamaño actual, y porque cada vez que std::vector<T>cambian el tamaño, reservan más espacio del que necesitan. Esto es para evitar que tengan que cambiar el tamaño cada vez que se inserta un nuevo elemento. Este comportamiento se puede cambiar proporcionando una costumbre allocator, ¡pero nunca sentí la necesidad de hacerlo!


Editar: Después de leer la respuesta de Zud a la pregunta, sentí que debería agregar esto:

La std::array<T>clase no es lo mismo que una matriz de C ++. std::array<T>es una envoltura muy delgada alrededor de las matrices de C ++, con el objetivo principal de ocultar el puntero al usuario de la clase (en C ++, las matrices se convierten implícitamente en punteros, a menudo con un efecto desalentador). La std::array<T>clase también almacena su tamaño (longitud), que puede ser muy útil.

Cierre Vaquero
fuente
55
Es "tan rápido" como usar una matriz incorporada asignada dinámicamente. Por otra parte, usar una matriz automática podría tener un rendimiento considerablemente diferente (y no solo durante la asignación, debido a los efectos de la localidad).
Ben Voigt
44
Para los vectores que no son bool en C ++ 11 y posteriores, puede llamar data()a std::vector<T>para obtener el puntero subyacente. También puede tomar la dirección del elemento 0 (se garantiza que funcionará con C ++ 11, probablemente funcionará con versiones anteriores).
Matt
16

Para enfatizar un punto hecho por @MatteoItalia, la diferencia de eficiencia es donde se almacenan los datos. La memoria de almacenamiento dinámico (requerida con vector) requiere una llamada al sistema para asignar memoria y esto puede ser costoso si está contando ciclos. La memoria de la pila (posible para array) es prácticamente "cero sobrecarga" en términos de tiempo, porque la memoria se asigna simplemente ajustando el puntero de la pila y se realiza solo una vez al ingresar a una función. La pila también evita la fragmentación de la memoria. Para estar seguro, std::arrayno siempre será en la pila; depende de dónde lo asigne, pero aún implicará una asignación de memoria menos del montón en comparación con el vector. Si tienes un

  • "matriz" pequeña (por ejemplo, menos de 100 elementos) - (una pila típica es de aproximadamente 8 MB, así que no asigne más de unos pocos KB en la pila o menos si su código es recursivo)
  • el tamaño será fijo
  • la duración está en el alcance de la función (o es un valor de miembro con la misma duración que la clase principal)
  • estás contando ciclos,

Definitivamente use un std::arraysobre un vector. Si alguno de esos requisitos no es cierto, utilice a std::vector.

Mark Lakata
fuente
3
Buena respuesta. "Sin duda, std :: array no siempre estará en la pila; depende de dónde lo asignes" Entonces, ¿cómo podría crear un std :: array que no esté en la pila con una gran cantidad de elementos?
Trilarion
44
@Trilarion usa new std::arrayo haz que sea miembro de una clase que usas 'new` para asignar.
Mark Lakata
Entonces, ¿esto significa que new std::arraytodavía espera saber su tamaño en el momento de la compilación y no puede cambiar su tamaño, pero aún vive en el montón?
Trilarion
Si. No es una ventaja importante del uso de new std::arrayvs new std::vector.
Mark Lakata
12

Si está considerando usar matrices multidimensionales, entonces hay una diferencia adicional entre std :: array y std :: vector. Un std :: array multidimensional tendrá los elementos empaquetados en la memoria en todas las dimensiones, tal como lo es el array de estilo ac. Un std :: vector multidimensional no se empaquetará en todas las dimensiones.

Dadas las siguientes declaraciones:

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 puntero al primer elemento en la matriz de estilo c (cConc) o std :: array (aConc) se puede iterar a través de toda la matriz agregando 1 a cada elemento precedente. Están bien embalados.

Un puntero al primer elemento en la matriz de vectores (vConc) o la matriz de punteros (ptrConc) solo puede iterarse a través de los primeros 5 elementos (en este caso), y luego hay 12 bytes (en mi sistema) de sobrecarga para el siguiente vector

Esto significa que una matriz std :: vector> inicializada como una matriz [3] [1000] será mucho más pequeña en la memoria que una inicializada como una matriz [1000] [3], y ambas serán más grandes en la memoria que una std: matriz asignada de cualquier manera.

Esto también significa que no puede simplemente pasar una matriz de vectores (o punteros) multidimensionales a, por ejemplo, openGL sin tener en cuenta la sobrecarga de memoria, sino que ingenuamente puede pasar una std :: array multidimensional a openGL y hacer que funcione.

psimpson
fuente
-17

Un vector es una clase contenedor mientras que una matriz es una memoria asignada.

Saif al Harthi
fuente
23
Su respuesta parece abordar std::vector<T>versus T[], pero la pregunta es acerca de std::vector<T>versus std::array<T>.
Keith Pinson el