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

Resuelto Zud asked hace 13 años • 6 respuestas

¿ 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 hace mi libro de texto es enumerar en qué se parecen.

Zud avatar Dec 13 '10 05:12 Zud
Aceptado

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 medio de un vector (se encarga de todo el trabajo de mover los siguientes elementos detrás de escena).

Dado que almacena los elementos en la memoria asignada en el montón, tiene cierta 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 en sí estará en la pila. Su tamaño debe conocerse en el momento de la compilación (se pasa como 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 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 std::vectory de los otros contenedores, por lo que puede usarlo fácilmente con algoritmos STL y demás. De todos modos, por la limitación misma del tamaño fijo, es mucho menos flexible que std::vector.

Para obtener una introducción std::array, consulte este artículo ; Para obtener una introducción rápida a std::vectorlas operaciones que son posibles en él, es posible que desee consultar su documentación .


  1. En realidad, creo que en el estándar se describen en términos de complejidad máxima 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 existe otro método para cumplir dichos requisitos aparte del uso de una matriz dinámica. Como lo indicó @Lucretiel, el estándar en realidad requiere que los elementos se almacenen de forma contigua, por lo que es una matriz dinámica, almacenada donde la coloca el asignador asociado.
Matteo Italia avatar Dec 12 '2010 23:12 Matteo Italia

Para enfatizar un punto planteado por @MatteoItalia, la diferencia de eficiencia es dónde se almacenan los datos. La memoria de montón (obligatoria con vector) requiere una llamada al sistema para asignar memoria y esto puede resultar costoso si cuenta ciclos. La memoria de pila (posible para array) tiene prácticamente "sobrecarga cero" en términos de tiempo, porque la memoria se asigna simplemente ajustando el puntero de la pila y se hace solo una vez al ingresar a una función. La pila también evita la fragmentación de la memoria. Sin duda, std::arrayno siempre estará 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 tiene 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 vida útil está en el alcance de la función (o es un valor de miembro con la misma vida útil que la clase principal)
  • estás contando ciclos,

Definitivamente use un std::arraysobre un vector. Si alguno de esos requisitos no se cumple, utilice un archivo std::vector.

Mark Lakata avatar Jun 29 '2015 18:06 Mark Lakata

Resumiendo la discusión anterior en una tabla para referencia rápida:

Matriz estilo C std::matriz estándar::vector
Tamaño Fijo/Estático Fijo/Estático Dinámica
Eficiencia de la memoria Más eficiente Más eficiente Menos eficiente
(puede duplicar su tamaño con una nueva asignación).
Proceso de copiar Iterar sobre elementos
o utilizarstd::copy()
Copia directa:a2 = a1; Copia directa:v2 = v1;
Pasando a funcionar Pasado por puntero
(tamaño no disponible en la función)
o comostd::span
Pasado por valor
o comostd::span
Pasado por valor
o comostd::span
Tamaño sizeof a1 / sizeof *a1
ostd::size(a1)
a1.size()
ostd::size(a1)
v1.size()
ostd::size(v1)
Caso de uso Para un acceso rápido y cuando
las inserciones/eliminaciones no son necesarias con frecuencia.
Igual que la matriz clásica pero
más segura y fácil de pasar y copiar.

Cuándo pueden ser necesarias adiciones o eliminaciones frecuentes
Frida Schenker avatar May 08 '2021 12:05 Frida Schenker

Si está considerando utilizar matrices multidimensionales, existe una diferencia adicional entre std::arrayy std::vector. Un multidimensional std::arraytendrá los elementos empaquetados en la memoria en todas las dimensiones, tal como lo es una matriz de estilo C. Un multidimensional std::vectorno estará empaquetado 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 de 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 anterior. Están muy apretados.

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

Esto significa que una std::vector<std::vector<int>>matriz inicializada como [3][1000]matriz tendrá una memoria mucho más pequeña que una inicializada como [1000][3]matriz, y ambas tendrán una memoria mayor que una std::arrayasignada en cualquier sentido.

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

psimpson avatar Mar 02 '2017 18:03 psimpson

Usando la std::vector<T>clase:

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

  • ...cambia de tamaño automáticamente 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 archivo std::vector, desplazando automáticamente el resto de los elementos hacia abajo.

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

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

  1. No tiene acceso confiable al puntero subyacente, lo que puede ser un problema si trabaja con 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, std::vector<T>s será un poco más grande que una matriz de C++ con la misma cantidad de elementos. Esto se debe a que necesitan realizar un seguimiento de una pequeña cantidad de información adicional, 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 de tamaño cada vez que se inserta un nuevo elemento. Este comportamiento se puede cambiar proporcionando un custom allocator, ¡pero nunca sentí la necesidad de hacerlo!


Editar: después de leer la respuesta de Zud a la pregunta, sentí que debí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 propósito principal de ocultar el puntero al usuario de la clase (en C++, las matrices se convierten implícitamente como punteros, a menudo con un efecto desalentador). La std::array<T>clase también almacena su tamaño (longitud), lo que puede resultar muy útil.

ClosureCowboy avatar Dec 12 '2010 23:12 ClosureCowboy