¿Alguien ha usado alguna vez la "ubicación nueva" de C ++? Si es así, ¿para qué? Me parece que solo sería útil en hardware mapeado en memoria.
c++
memory-management
new-operator
placement-new
Head Geek
fuente
fuente
p = pt
y usar el operador de asignación de enPoint
lugar de hacerlonew(&p) Point(pt)
? Me pregunto las diferencias entre los dos. ¿Llamaría el primerooperator=
a Point, mientras que el segundo llamará al constructor de copiaPoint
? pero todavía no estoy muy claro por qué uno es mejor que el otro.U::operator=
se acaba de llamar.Respuestas:
La nueva ubicación le permite construir un objeto en la memoria que ya está asignado.
Es posible que desee hacer esto para la optimización cuando necesite construir múltiples instancias de un objeto, y es más rápido no reasignar memoria cada vez que necesite una nueva instancia. En cambio, podría ser más eficiente realizar una única asignación para un trozo de memoria que puede contener múltiples objetos, aunque no quiera usarlos todos a la vez.
DevX da un buen ejemplo :
También es posible que desee asegurarse de que no puede haber un error de asignación en una determinada parte del código crítico (por ejemplo, en el código ejecutado por un marcapasos). En ese caso, desearía asignar memoria antes, luego usar la ubicación nueva dentro de la sección crítica.
Desasignación en colocación nuevo
No debe desasignar todos los objetos que usan el búfer de memoria. En su lugar, debe eliminar [] solo el búfer original. Tendría que llamar a los destructores de sus clases manualmente. Para una buena sugerencia sobre esto, consulte las Preguntas frecuentes de Stroustrup sobre: ¿Hay una "eliminación de ubicación" ?
fuente
delete[]
alchar
búfer original . El uso de la colocaciónnew
ha finalizado la vida útil de loschar
objetos originales al reutilizar su almacenamiento. Si ahora llamadelete[] buf
al tipo dinámico de los objetos apuntados, ya no coincide con su tipo estático, entonces tiene un comportamiento indefinido. Es más consistente usaroperator new
/operator delete
asignar memoria cruda destinada para su uso por ubicaciónnew
.#include <new>
.Lo usamos con grupos de memoria personalizados. Solo un boceto:
Ahora puede agrupar objetos en una sola arena de memoria, seleccionar un asignador que sea muy rápido pero no desasigna, use el mapeo de memoria y cualquier otra semántica que desee imponer eligiendo el grupo y pasándolo como argumento a la ubicación de un objeto nuevo operador
fuente
allocate()
alguna parte?Es útil si desea separar la asignación de la inicialización. STL utiliza la colocación nueva para crear elementos de contenedor.
fuente
Lo he usado en programación en tiempo real. Por lo general , no queremos realizar ninguna asignación dinámica (o desasignación) después de que se inicie el sistema, porque no hay garantía de cuánto tiempo llevará eso.
Lo que puedo hacer es preasignar una gran cantidad de memoria (lo suficientemente grande como para contener cualquier cantidad de lo que la clase pueda requerir). Luego, una vez que descubro en el tiempo de ejecución cómo construir las cosas, la colocación nueva se puede usar para construir objetos justo donde los quiero. Una situación en la que sé que lo usé fue para ayudar a crear un búfer circular heterogéneo .
Ciertamente no es para los débiles de corazón, pero es por eso que hacen que la sintaxis sea un poco retorcida.
fuente
Lo he usado para construir objetos asignados en la pila a través de alloca ().
enchufe descarado: escribí un blog al respecto aquí .
fuente
boost::array
. ¿Puedes ampliar eso un poco?Head Geek: ¡BINGO! Lo tienes totalmente, eso es exactamente para lo que es perfecto. En muchos entornos integrados, las restricciones externas y / o el escenario de uso general obligan al programador a separar la asignación de un objeto de su inicialización. Juntos, C ++ llama a esto "instanciación"; pero siempre que la acción del constructor se debe invocar explícitamente SIN asignación dinámica o automática, la colocación nueva es la forma de hacerlo. También es la manera perfecta de localizar un objeto global de C ++ que está anclado a la dirección de un componente de hardware (E / S mapeadas en memoria), o para cualquier objeto estático que, por cualquier razón, debe residir en una dirección fija.
fuente
Lo he usado para crear una clase Variant (es decir, un objeto que puede representar un valor único que puede ser uno de varios tipos diferentes).
Si todos los tipos de valores admitidos por la clase Variant son tipos POD (por ejemplo, int, float, double, bool), entonces una unión de estilo C etiquetada es suficiente, pero si desea que algunos de los tipos de valor sean objetos C ++ ( por ejemplo, std :: string), la función de unión C no funcionará, ya que los tipos de datos que no son POD pueden no declararse como parte de una unión.
Entonces, en su lugar, asigno una matriz de bytes que es lo suficientemente grande (por ejemplo, sizeof (the_largest_data_type_I_support)) y uso la ubicación nueva para inicializar el objeto C ++ apropiado en esa área cuando la Variante está configurada para contener un valor de ese tipo. (Y la eliminación de la ubicación de antemano al cambiar de un tipo de datos diferente a POD, por supuesto)
fuente
new
para inicializar su subclase no POD. Ref: stackoverflow.com/a/33289972/2757035 Reinventar esta rueda usando una matriz de bytes arbitrariamente grande es una impresionante pieza de acrobacia, pero parece totalmente innecesaria. Entonces, ¿qué me he perdido? :)La colocación nueva también es muy útil cuando se serializa (digamos con boost :: serialization). En 10 años de c ++, este es solo el segundo caso para el que necesito una nueva ubicación (tercero si incluye entrevistas :)).
fuente
También es útil cuando desea reinicializar estructuras globales o estáticamente asignadas.
La antigua forma de C se usaba
memset()
para establecer todos los elementos en 0. No se puede hacer eso en C ++ debido a vtables y constructores de objetos personalizados.Así que a veces uso lo siguiente
fuente
Creo que esto no ha sido resaltado por ninguna respuesta, pero otro buen ejemplo y uso para la nueva ubicación es reducir la fragmentación de la memoria (mediante el uso de grupos de memoria). Esto es especialmente útil en sistemas integrados y de alta disponibilidad. En este último caso es especialmente importante porque para un sistema que tiene que funcionar 24/365 días es muy importante no tener fragmentación. Este problema no tiene nada que ver con la pérdida de memoria.
Incluso cuando se utiliza una muy buena implementación de malloc (o una función de administración de memoria similar) es muy difícil lidiar con la fragmentación durante mucho tiempo. En algún momento, si no gestiona inteligentemente las llamadas de reserva / liberación de memoria, podría terminar con muchos pequeños vacíos que son difíciles de reutilizar (asignar a nuevas reservas). Por lo tanto, una de las soluciones que se utilizan en este caso es utilizar un grupo de memoria para asignar de antemano la memoria para los objetos de la aplicación. Después, cada vez que necesite memoria para algún objeto, simplemente use la nueva ubicación para crear un nuevo objeto en la memoria ya reservada.
De esta manera, una vez que se inicia su aplicación, ya tiene toda la memoria necesaria reservada. Toda la nueva reserva / liberación de memoria va a los grupos asignados (puede tener varios grupos, uno para cada clase de objeto diferente). En este caso, no ocurre fragmentación de la memoria, ya que no habrá brechas y su sistema puede funcionar durante períodos muy largos (años) sin sufrir fragmentación.
Vi esto en la práctica especialmente para el VxWorks RTOS ya que su sistema de asignación de memoria predeterminado sufre mucho de fragmentación. Por lo tanto, la asignación de memoria a través del método estándar nuevo / malloc estaba básicamente prohibido en el proyecto. Todas las reservas de memoria deben ir a un grupo de memoria dedicado.
fuente
En realidad, es necesario implementar cualquier tipo de estructura de datos que asigne más memoria que la mínimamente requerida para el número de elementos insertados (es decir, cualquier otra cosa que no sea una estructura vinculada que asigna un nodo a la vez).
Contenedores para llevar gusta
unordered_map
,vector
odeque
. Todos estos asignan más memoria de la mínima requerida para los elementos que ha insertado hasta ahora para evitar requerir una asignación de montón para cada inserción. Usemosvector
como el ejemplo más simple.Cuando tu lo hagas:
... eso en realidad no construye mil Foos. Simplemente asigna / reserva memoria para ellos. Si
vector
no usó la ubicación nueva aquí, sería una construcción predeterminada enFoos
todo el lugar, así como tener que invocar sus destructores incluso para elementos que nunca insertó en primer lugar.Asignación! = Construcción, Liberación! = Destrucción
En términos generales, para implementar muchas estructuras de datos como las anteriores, no puede tratar la asignación de memoria y la construcción de elementos como una cosa indivisible, y tampoco puede tratar la liberación de memoria y la destrucción de elementos como una cosa indivisible.
Tiene que haber una separación entre estas ideas para evitar invocar innecesariamente constructores y destructores innecesariamente a izquierda y derecha, y es por eso que la biblioteca estándar separa la idea de
std::allocator
(que no construye ni destruye elementos cuando asigna / libera memoria *) los contenedores que lo usan que construyen elementos manualmente mediante la colocación de elementos nuevos y destruyen elementos manualmente mediante invocaciones explícitas de destructores.De todos modos, tiendo a usarlo mucho ya que he escrito una serie de contenedores C ++ de propósito general que cumplen con los estándares que no se pudieron construir en términos de los existentes. Entre ellos se incluye una pequeña implementación de vectores que construí hace un par de décadas para evitar las asignaciones de almacenamiento dinámico en casos comunes, y un trie de memoria eficiente (no asigna un nodo a la vez). En ambos casos, realmente no pude implementarlos usando los contenedores existentes, por lo que tuve que usar
placement new
para evitar invocar superfluos constructores y destructores en cosas innecesarias a izquierda y derecha.Naturalmente, si alguna vez trabaja con asignadores personalizados para asignar objetos individualmente, como una lista gratuita, entonces generalmente también querrá usar
placement new
, como este (ejemplo básico que no molesta con la seguridad de excepción o RAII):fuente
Es útil si está construyendo un núcleo: ¿dónde coloca el código del núcleo que leyó del disco o de la tabla de páginas? Necesitas saber a dónde saltar.
O en otras circunstancias muy raras, como cuando tiene un montón de espacio asignado y desea colocar algunas estructuras una detrás de la otra. Se pueden empaquetar de esta manera sin la necesidad del operador offsetof (). Sin embargo, también hay otros trucos para eso.
También creo que algunas implementaciones de STL hacen uso de la colocación nueva, como std :: vector. Asignan espacio para 2 ^ n elementos de esa manera y no necesitan reasignar siempre.
fuente
Se usa
std::vector<>
porquestd::vector<>
normalmente asigna más memoria de la que hayobjects
en elvector<>
.fuente
Lo he usado para almacenar objetos con archivos asignados en memoria.
El ejemplo específico era una base de datos de imágenes que procesaba una gran cantidad de imágenes grandes (más de las que cabían en la memoria).
fuente
Lo he visto como un ligero truco de rendimiento para un puntero de "tipo dinámico" (en la sección "Bajo el capó"):
fuente
void*
toma 8 bytes. Es un poco tonto señalar un byte de ochovoid*
en un bytebool
. Pero es completamente posible superponerlobool
en elvoid*
, al igual que aunion { bool b; void* v }
. Necesita alguna forma de saber que lo que llamó avoid*
es en realidad unbool
(o unshort
, o unfloat
, etc.). El artículo al que me vinculé describe cómo hacerlo. Y, para responder a la pregunta original, la ubicaciónnew
es la característica utilizada para crear unbool
(u otro tipo) dondevoid*
se espera un (los modelos se utilizan para obtener / modificar el valor más adelante).Lo he usado para crear objetos basados en la memoria que contiene mensajes recibidos de la red.
fuente
En general, la colocación nueva se utiliza para deshacerse del costo de asignación de una 'nueva normal'.
Otro escenario donde lo usé es un lugar donde quería tener acceso al puntero a un objeto que aún estaba por construirse, para implementar un singleton por documento.
fuente
Puede ser útil cuando se usa memoria compartida, entre otros usos ... Por ejemplo: http://www.boost.org/doc/libs/1_51_0/doc/html/interprocess/synchronization_mechanisms.html#interprocess.synchronization_mechanisms.conditions. condiciones_anónimo_ejemplo
fuente
El único lugar donde me he encontrado es en contenedores que asignan un búfer contiguo y luego lo llenan con objetos según sea necesario. Como se mencionó, std :: vector podría hacer esto, y sé que algunas versiones de MFC CArray y / o CList hicieron esto (porque ahí es donde lo encontré por primera vez). El método de sobreasignación de búfer es una optimización muy útil, y la colocación nueva es prácticamente la única forma de construir objetos en ese escenario. También se usa a veces para construir objetos en bloques de memoria asignados fuera de su código directo.
Lo he usado en una capacidad similar, aunque no aparece a menudo. Sin embargo, es una herramienta útil para la caja de herramientas de C ++.
fuente
Los motores de script pueden usarlo en la interfaz nativa para asignar objetos nativos a partir de scripts. Consulte Angelscript (www.angelcode.com/angelscript) para ver ejemplos.
fuente
Consulte el archivo fp.h en el proyecto xll en http://xll.codeplex.com. Resuelve el problema de "falta de garantía injustificada con el compilador" para las matrices que desean llevar sus dimensiones consigo.
fuente
Aquí está el uso asesino para el constructor in situ de C ++: alineación a una línea de caché, así como a otras potencias de 2 límites. Aquí está mi algoritmo de alineación de puntero ultrarrápido a cualquier potencia de 2 límites con 5 o menos instrucciones de ciclo único :
Ahora, eso no solo pone una sonrisa en tu cara (:-). I ♥♥♥ C ++ 1x
fuente