std::unique_ptr
tiene soporte para arreglos, por ejemplo:
std::unique_ptr<int[]> p(new int[10]);
pero es necesario? probablemente sea más conveniente usar std::vector
o std::array
.
¿Encuentras algún uso para esa construcción?
pantano
std::unique_ptr
tiene soporte para arreglos, por ejemplo:
std::unique_ptr<int[]> p(new int[10]);
pero es necesario? probablemente sea más conveniente usar std::vector
o std::array
.
¿Encuentras algún uso para esa construcción?
Nicolás Bolas
Algunas personas no pueden darse el lujo de usar std::vector
, incluso con asignadores. Algunas personas necesitan una matriz de tamaño dinámico, por lo que std::array
Está fuera. Y algunas personas obtienen sus matrices de otro código que se sabe que devuelve una matriz; y ese código no se va a reescribir para devolver un vector
o algo.
Al permitir unique_ptr<T[]>
usted atiende esas necesidades.
En resumen, usas unique_ptr<T[]>
Cuando usted necesitar para. Cuando las alternativas simplemente no van a funcionar para usted. Es una herramienta de último recurso.
@NoSenseEtAl: No estoy seguro de qué parte de “algunas personas no pueden hacer eso” se te escapa. Algunos proyectos tienen requisitos muy específicos, y entre ellos puede estar “no puedes usar vector
“. Puede argumentar si esos son requisitos razonables o no, pero no puede negar que existe.
– Nicolás Bolas
30 de mayo de 2013 a las 15:48
No hay ninguna razón en el mundo por la que alguien no pueda usar std::vector
si pueden usar std::unique_ptr
.
– Ruta Millas
29 de abril de 2014 a las 14:48
aquí hay una razón para no usar vector: sizeof(std::vector
– Arvid
12/09/2014 a las 22:34
@DanNissenbaum Estos proyectos existen. Algunas industrias que están bajo un escrutinio muy estricto, como por ejemplo, la aviación o la defensa, la biblioteca estándar está fuera de los límites porque es difícil verificar y demostrar que es correcta para cualquier organismo rector que establezca las regulaciones. Puede argumentar que la biblioteca estándar está bien probada y estaría de acuerdo con usted, pero usted y yo no hacemos las reglas.
– Emilio L.
18/09/2014 a las 13:37
@DanNissenbaum Además, algunos sistemas duros en tiempo real no pueden usar la asignación de memoria dinámica en absoluto, ya que la demora que causa una llamada al sistema podría no estar teóricamente limitada y no puede probar el comportamiento en tiempo real del programa. O el límite puede ser demasiado grande, lo que supera su límite de WCET. Aunque no es aplicable aquí, ya que no usarían unique_ptr
tampoco, pero ese tipo de proyectos realmente existen.
– Emilio L.
18/09/2014 a las 13:42
Seudónimo
Hay compensaciones, y usted elige la solución que coincida con lo que desea. La parte superior de mi cabeza:
Tamaño inicial
vector
y unique_ptr<T[]>
permitir que el tamaño se especifique en tiempo de ejecuciónarray
solo permite especificar el tamaño en tiempo de compilaciónCambiar el tamaño
array
y unique_ptr<T[]>
no permitir cambiar el tamañovector
lo haceAlmacenamiento
vector
y unique_ptr<T[]>
almacenar los datos fuera del objeto (típicamente en el montón)array
almacena los datos directamente en el objetoProceso de copiar
array
y vector
permitir copiarunique_ptr<T[]>
no permite copiarintercambiar/mover
vector
y unique_ptr<T[]>
tener O(1) tiempo swap
y mover operacionesarray
tiene tiempo O(n) swap
y mover operaciones, donde n es el número de elementos en la matrizInvalidación de puntero/referencia/iterador
array
garantiza que los punteros, las referencias y los iteradores nunca se invalidarán mientras el objeto esté activo, incluso en swap()
unique_ptr<T[]>
no tiene iteradores; punteros y referencias sólo son invalidados por swap()
mientras el objeto está activo. (Después del intercambio, los punteros apuntan a la matriz con la que intercambió, por lo que aún son “válidos” en ese sentido).vector
puede invalidar punteros, referencias e iteradores en cualquier reasignación (y proporciona algunas garantías de que la reasignación solo puede ocurrir en ciertas operaciones).Compatibilidad con conceptos y algoritmos
array
y vector
ambos son contenedoresunique_ptr<T[]>
no es un contenedorDebo admitir que esto parece una oportunidad para refactorizar con un diseño basado en políticas.
No estoy seguro de entender lo que quieres decir en el contexto de invalidación de puntero. ¿Se trata de punteros a los objetos mismos o punteros a los elementos? ¿O algo mas? ¿Qué tipo de garantía obtiene de una matriz que no obtiene de un vector?
– jogojapan
29 de mayo de 2013 a las 2:52
Suponga que tiene un iterador, un puntero o una referencia a un elemento de un vector
. Luego aumenta el tamaño o la capacidad de ese vector
tal que fuerza una reasignación. Entonces ese iterador, puntero o referencia ya no apunta a ese elemento del vector
. Esto es lo que entendemos por “invalidación”. Este problema no le pasa a array
, porque no hay “reasignación”. En realidad, acabo de notar un detalle con eso, y lo he editado para adaptarlo.
– Seudónimo
29 de mayo de 2013 a las 3:33
Ok, no puede haber invalidación como resultado de la reasignación en una matriz o unique_ptr<T[]>
porque no hay reasignación. Pero, por supuesto, cuando la matriz queda fuera del alcance, los punteros a elementos específicos seguirán siendo invalidados.
– jogojapan
29 de mayo de 2013 a las 3:38
@rubenvb Claro que puede, pero no puede (decir) usar bucles for basados en rango directamente. Por cierto, a diferencia de un normal T[]
el tamaño (o información equivalente) debe estar dando vueltas en algún lugar para operator delete[]
para destruir correctamente los elementos de la matriz. Sería bueno si el programador tuviera acceso a eso.
– Seudónimo
30 de mayo de 2013 a las 0:09
@Aidiakapi C++ requiere que si delete[]
una matriz de objetos que tienen destructores, los destructores se ejecutan. Por esa razón, el tiempo de ejecución de C++ ya necesita conocer el tamaño real de la mayoría de las matrices que se han asignado de esa manera. Ahora, las implementaciones decentes de C++ optimizan los destructores si los objetos en la matriz no tienen destructor (por ejemplo, un tipo básico) o un destructor que no hace nada. Sin embargo, normalmente no optimizan el asignador de memoria para este caso. Podría pasar, pero no pasa. Así que la información del tamaño está ahí.
– Seudónimo
28 de mayo de 2015 a las 2:31
Carlos Salvia
Una de las razones por las que podría usar un unique_ptr
es si no quiere pagar el costo del tiempo de ejecución de inicialización de valor la matriz
std::vector<char> vec(1000000); // allocates AND value-initializes 1000000 chars
std::unique_ptr<char[]> p(new char[1000000]); // allocates storage for 1000000 chars
los std::vector
constructor y std::vector::resize()
inicializará el valor T
– pero new
no hare eso si T
es un POD.
Consulte Objetos inicializados por valor en C++ 11 y constructor std::vector
Tenga en cuenta que vector::reserve
no es una alternativa aquí: ¿Es seguro acceder al puntero sin formato después de std::vector::reserve?
Es la misma razón por la que un programador de C podría elegir malloc
sobre calloc
.
Pero esta razón no es la única solución.
– Ruslán
23 de agosto de 2016 a las 13:33
@Ruslan En la solución vinculada, los elementos de la matriz dinámica todavía tienen un valor inicializado, pero la inicialización del valor no hace nada. Estoy de acuerdo en que un optimizador que no se da cuenta de que no hacer nada 1000000 veces puede implementarse sin código no vale un centavo, pero uno podría preferir no depender de esta optimización en absoluto.
–Marc van Leeuwen
25/03/2017 a las 13:30
otra posibilidad más es proporcionar a std::vector
un asignador personalizado que evita la construcción de tipos que son std::is_trivially_default_constructible
y destrucción de objetos que son std::is_trivially_destructible
aunque estrictamente esto viola el estándar C++ (ya que tales tipos no se inicializan por defecto).
– Gualterio
25 mayo 2017 a las 21:37
también std::unique_ptr
no proporciona ninguna comprobación vinculada, al contrario de muchos std::vector
implementaciones.
– diapiro
26 de diciembre de 2017 a las 17:09
@diapir No se trata de la implementación: std::vector
es requerido por el estándar para comprobar los límites en .at()
. Supongo que quiso decir que algunas implementaciones tienen modos de depuración que se registrarán .operator[]
también, pero considero que es inútil para escribir un buen código portátil.
– subrayado_d
26 de junio de 2020 a las 8:26
Andy merodea
Un std::vector
se puede copiar, mientras que unique_ptr<int[]>
permite expresar la propiedad única de la matriz. std::array
por otro lado, requiere que el tamaño se determine en tiempo de compilación, lo que puede ser imposible en algunas situaciones.
novato
Scott Meyers tiene esto que decir en Eficaz Modern C++
La existencia de
std::unique_ptr
porque las matrices deberían ser solo de interés intelectual para usted, porquestd::array
,
std::vector
,std::string
son prácticamente siempre mejores opciones de estructura de datos que las matrices sin formato. Acerca de la única situación que puedo concebir cuando unstd::unique_ptr<T[]>
tendría sentido sería cuando está utilizando una API similar a C que devuelve un puntero sin procesar a una matriz de montón de la que asume la propiedad.
Sin embargo, creo que la respuesta de Charles Salvia es relevante: que std::unique_ptr<T[]>
es la única forma de inicializar una matriz vacía cuyo tamaño no se conoce en tiempo de compilación. ¿Qué diría Scott Meyers sobre esta motivación para usar std::unique_ptr<T[]>
?
Parece que simplemente no imaginó algunos casos de uso, a saber, un búfer cuyo tamaño es fijo pero desconocido en el momento de la compilación, y/o un búfer para el que no permitimos copias. También existe la eficiencia como una posible razón para preferirlo a vector
stackoverflow.com/a/24852984/2436175.
– Antonio
17/07/2018 a las 21:43
Jorge
Contrariamente a std::vector
y std::array
, std::unique_ptr
puede poseer un puntero NULL.
Esto es útil cuando se trabaja con API de C que esperan una matriz o NULL:
void legacy_func(const int *array_or_null);
void some_func() {
std::unique_ptr<int[]> ptr;
if (some_condition) {
ptr.reset(new int[10]);
}
legacy_func(ptr.get());
}
Parece que simplemente no imaginó algunos casos de uso, a saber, un búfer cuyo tamaño es fijo pero desconocido en el momento de la compilación, y/o un búfer para el que no permitimos copias. También existe la eficiencia como una posible razón para preferirlo a vector
stackoverflow.com/a/24852984/2436175.
– Antonio
17/07/2018 a las 21:43
No puedo estar en desacuerdo con el espíritu de la respuesta aceptada lo suficiente. ¿”Una herramienta de último recurso”? ¡Lejos de ahi!
A mi modo de ver, una de las características más sólidas de C++ en comparación con C y otros lenguajes similares es la capacidad de expresar restricciones para que se puedan verificar en el momento de la compilación y se pueda evitar el mal uso accidental. Entonces, cuando diseñe una estructura, pregúntese qué operaciones debe permitir. Todos los demás usos deben estar prohibidos, y es mejor si tales restricciones se pueden implementar de forma estática (en tiempo de compilación) para que el mal uso resulte en una falla de compilación.
Entonces, cuando se necesita una matriz, las respuestas a las siguientes preguntas especifican su comportamiento: 1. ¿Su tamaño es a) dinámico en tiempo de ejecución, o b) estático, pero solo conocido en tiempo de ejecución, o c) estático y conocido en tiempo de compilación? 2. ¿Se puede asignar la matriz en la pila o no?
Y según las respuestas, esto es lo que veo como la mejor estructura de datos para una matriz de este tipo:
Dynamic | Runtime static | Static
Stack std::vector unique_ptr<T[]> std::array
Heap std::vector unique_ptr<T[]> unique_ptr<std::array>
si, creo unique_ptr<std::array>
también debe considerarse, y tampoco es una herramienta de último recurso. Piensa qué encaja mejor con tu algoritmo.
Todos estos son compatibles con las API de C simples a través del puntero sin procesar a la matriz de datos (vector.data()
/ array.data()
/ uniquePtr.get()
).
PD Además de las consideraciones anteriores, también hay una de propiedad: std::array
y std::vector
tienen semántica de valor (tienen soporte nativo para copiar y pasar por valor), mientras que unique_ptr<T[]>
solo se puede mover (hace cumplir la propiedad única). Cualquiera de los dos puede ser útil en diferentes escenarios. Por el contrario, los arreglos estáticos simples (int[N]
) y matrices dinámicas simples (new int[10]
) no ofrecen ninguno y, por lo tanto, deben evitarse si es posible, lo que debería ser posible en la gran mayoría de los casos. Si eso no fuera suficiente, las matrices dinámicas simples tampoco ofrecen ninguna forma de consultar su tamaño: una oportunidad adicional para la corrupción de la memoria y los agujeros de seguridad.
Para completar, debo señalar que no hay
std::shared_ptr<T[]>
, pero debería haberlo, y probablemente lo habrá en C++14 si alguien se molesta en redactar una propuesta. Mientras tanto, siempre hayboost::shared_array
.– Seudónimo
30 de mayo de 2013 a las 0:23
std::shared_ptr
– Chen Li
1 de noviembre de 2018 a las 2:04
Puede encontrar múltiples formas de hacer cualquier cosa en una computadora. Esta construcción tiene uso, especialmente en una ruta activa, porque erradica la sobrecarga de las operaciones de contenedores si sabe exactamente cómo apuntar a su matriz. Además, crea matrices de caracteres sin ninguna duda de almacenamiento contiguo.
– kevr
19 de abril de 2020 a las 17:13
Encontré esto útil para interoperar con estructuras C donde un miembro de la estructura determina su tamaño. Quiero que la memoria se desasigne automáticamente, pero no hay ningún tipo del tamaño adecuado para la desasignación, así que utilicé una matriz de caracteres.
– Fuzzy Tew
5 de julio de 2020 a las 17:08
Un detalle importante es que si
std::unique_ptr<T[]>
se utiliza, también se debe proporcionar un eliminador personalizado, ya que, de forma predeterminada, el puntero inteligente solo llamadelete
y nodelete[]
. Ver es.cppreference.com/w/cpp/memory/unique_ptr/unique_ptr– David Toth
hace 2 días