¿Qué usos hay para “colocación nueva”?

12 minutos de lectura

¿Que usos hay para colocacion nueva
Jefe friki

¿Alguien aquí 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.

  • Esta es solo la información que he estado buscando, para llamar a los constructores de objetos en los grupos de memoria asignados de impulso. (Con la esperanza de que estas palabras clave faciliten que alguien las encuentre en el futuro).

    – Bob acto secundario

    14 de septiembre de 2011 a las 10:19

  • se usa en el artículo de Wikipedia C++11 en el constructor de una unión.

    – Hola Adios

    5 de noviembre de 2015 a las 10:15

  • @HelloGoodbye, ¡interesante! En el artículo que vinculaste, ¿por qué no puedes simplemente hacer p = pt y utilizar el operador de asignación de Point en lugar de hacer new(&p) Point(pt) ? Me pregunto las diferencias entre los dos. ¿Llamaría el primero operator= on Point, mientras que este último llama al constructor de copia de Point ? pero todavía no tengo muy claro por qué uno es mejor que el otro.

    – Andrei-Niculae Petre

    28 de noviembre de 2018 a las 20:29

  • @ Andrei-NiculaePetre No he usado la ubicación nueva, pero supongo que debería usarla, junto con el constructor de copia, si actualmente no tiene un objeto de esa clase; de ​​lo contrario, debe usar el operador de asignación de copia. A menos que la clase sea trivial; entonces no importa cuál de ellos uses. Lo mismo ocurre con la destrucción del objeto. Si no se maneja esto correctamente para las clases no triviales, es muy probable que se produzca un comportamiento extraño e incluso podría causar comportamiento indefinido en algunas situaciones.

    – Hola Adios

    28/11/2018 a las 23:30

  • @Andrei-NiculaePetre En realidad, encuentro el ejemplo en el artículo de Wikipedia bastante mal, ya que simplemente asume que no existe ningún objeto anterior y que necesitan construir uno. Este no es el caso si U::operator= acaba de ser llamado.

    – Hola Adios

    28 de noviembre de 2018 a las 23:44

1647644592 950 ¿Que usos hay para colocacion nueva
Brian R. Bondy

La ubicación nueva 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 varias instancias de un objeto, y es más rápido no reasignar memoria cada vez que necesita una nueva instancia. En su lugar, podría ser más eficiente realizar una única asignación para una parte de la memoria que puede contener varios objetos, aunque no desee utilizarlos todos a la vez.

DevX da una buen ejemplo:

El C++ estándar también admite la colocación de un nuevo operador, que construye un objeto en un búfer preasignado. Esto es útil cuando se crea un grupo de memoria, un recolector de elementos no utilizados o simplemente cuando el rendimiento y la seguridad de excepción son primordiales (no hay peligro de falla en la asignación ya que la memoria ya se ha asignado, y la construcción de un objeto en un búfer preasignado toma menos tiempo) :

char *buf  = new char[sizeof(string)]; // pre-allocated buffer
string *p = new (buf) string("hi");    // placement new
string *q = new string("hi");          // ordinary heap allocation

También puede querer asegurarse de que no haya fallas en la asignación en una determinada parte del código crítico (por ejemplo, en el código ejecutado por un marcapasos). En ese caso, querrá asignar memoria antes y luego usar la ubicación nueva dentro de la sección crítica.

Desasignación en ubicación nueva

No debe desasignar todos los objetos que utilizan el búfer de memoria. En su lugar, debe eliminar[] sólo el búfer original. Luego tendría que llamar a los destructores de sus clases manualmente. Para obtener una buena sugerencia al respecto, consulte las Preguntas frecuentes de Stroustrup sobre: ¿Hay una “eliminación de ubicación”??

  • No está en desuso ya que necesita esta función para implementar de manera eficiente los objetos de contenedor (como el vector). Sin embargo, si no está creando su propio contenedor, no necesita usar esta función.

    – Martín York

    21 de octubre de 2008 a las 21:19

  • También es muy importante recordar #include , de lo contrario, podría tener algunos dolores de cabeza terribles en algunas plataformas que no reconocen automáticamente la ubicación nueva

    – Ramón Zarazúa B.

    24 de septiembre de 2009 a las 18:28

  • Estrictamente, es un comportamiento indefinido llamar delete[] en el original char buffer. Usando la colocación new ha terminado la vida útil del original char objetos reutilizando su almacenamiento. Si ahora llamas delete[] buf el tipo dinámico de los objetos señalados ya no coincide con su tipo estático, por lo que tiene un comportamiento indefinido. Es más consistente usar operator new/operator delete para asignar memoria sin procesar destinada a su uso por ubicación new.

    –CB Bailey

    21 de marzo de 2010 a las 15:10

  • Definitivamente me saltaría el uso del montón en un marcapasos 🙂

    – Eli Bendersky

    9 de enero de 2011 a las 8:42

  • @RamonZarazua Cabecera equivocada, es #include <new>.

    – bit2shift

    26 de abril de 2016 a las 3:46

1647644592 854 ¿Que usos hay para colocacion nueva
Don Wakefield

Lo usamos con grupos de memoria personalizados. Solo un boceto:

class Pool {
public:
    Pool() { /* implementation details irrelevant */ };
    virtual ~Pool() { /* ditto */ };

    virtual void *allocate(size_t);
    virtual void deallocate(void *);

    static Pool::misc_pool() { return misc_pool_p; /* global MiscPool for general use */ }
};

class ClusterPool : public Pool { /* ... */ };
class FastPool : public Pool { /* ... */ };
class MapPool : public Pool { /* ... */ };
class MiscPool : public Pool { /* ... */ };

// elsewhere...

void *pnew_new(size_t size)
{
   return Pool::misc_pool()->allocate(size);
}

void *pnew_new(size_t size, Pool *pool_p)
{
   if (!pool_p) {
      return Pool::misc_pool()->allocate(size);
   }
   else {
      return pool_p->allocate(size);
   }
}

void pnew_delete(void *p)
{
   Pool *hp = Pool::find_pool(p);
   // note: if p == 0, then Pool::find_pool(p) will return 0.
   if (hp) {
      hp->deallocate(p);
   }
}

// elsewhere...

class Obj {
public:
   // misc ctors, dtors, etc.

   // just a sampling of new/del operators
   void *operator new(size_t s)             { return pnew_new(s); }
   void *operator new(size_t s, Pool *hp)   { return pnew_new(s, hp); }
   void operator delete(void *dp)           { pnew_delete(dp); }
   void operator delete(void *dp, Pool*)    { pnew_delete(dp); }

   void *operator new[](size_t s)           { return pnew_new(s); }
   void *operator new[](size_t s, Pool* hp) { return pnew_new(s, hp); }
   void operator delete[](void *dp)         { pnew_delete(dp); }
   void operator delete[](void *dp, Pool*)  { pnew_delete(dp); }
};

// elsewhere...

ClusterPool *cp = new ClusterPool(arg1, arg2, ...);

Obj *new_obj = new (cp) Obj(arg_a, arg_b, ...);

Ahora puede agrupar objetos en un solo campo de memoria, seleccionar un asignador que sea muy rápido pero que no desasigne, usar 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.

  • Sí. Nos volvemos bastante inteligentes al respecto, pero está fuera de tema para esta pregunta.

    –Don Wakefield

    28 de enero de 2011 a las 20:28

  • @jdkoftinoff, ¿tiene algún enlace a una muestra de código real? me parece muy interesante!

    – Víctor

    14 de enero de 2017 a las 10:26

  • @DonWakefield ¿Cómo maneja la alineación en este grupo? ¿No deberías pasar la alineación como un argumento para allocate() ¿algun lado?

    – Mijaíl Vasiliev

    13 de junio de 2018 a las 17:16

  • @MikhailVasilyev, en una implementación real, por supuesto, manejaría eso. Solo código de ejemplo.

    –Don Wakefield

    13 de junio de 2018 a las 17:36

  • ¿Qué sucede si la ubicación es una dirección no válida, digamos 0x0?

    – charlie

    2 de octubre de 2019 a las 4:18

¿Que usos hay para colocacion nueva
MSN

Es útil si desea separar la asignación de la inicialización. STL usa la ubicación nueva para crear elementos contenedores.

¿Que usos hay para colocacion nueva
TED

Lo he usado en la programación en tiempo real. normalmente no desea realizar una asignación dinámica (o desasignación) después de que se inicie el sistema, porque no hay garantía de cuánto tiempo tomará.

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 tiempo de ejecución cómo construir las cosas, la ubicació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 heterogéneo búfer circular.

Ciertamente no es para los débiles de corazón, pero es por eso que hacen que la sintaxis sea un poco retorcida.

1647644593 336 ¿Que usos hay para colocacion nueva
Ferruccio

Lo he usado para construir objetos asignados en la pila a través de alloca().

enchufe desvergonzado: escribí un blog sobre eso aquí.

  • artículo interesante, pero no estoy seguro de entender la ventaja de usar esto sobre boost::array. ¿Puedes ampliar eso un poco?

    – GrahamS

    10 de febrero de 2011 a las 11:52

  • boost::array requiere que el tamaño de la matriz sea una constante de tiempo de compilación. Esto no tiene esa limitación.

    – Ferruccio

    10 de febrero de 2011 a las 12:26

  • @Ferruccio Esto es genial, me di cuenta de que su macro es un poco insegura, es decir, el tamaño podría ser una expresión. Si se pasa x+1, por ejemplo, lo expandiría a sizeof(type) * x + 1, lo que sería incorrecto. Necesita poner entre paréntesis su macro para que sea más seguro.

    – Benj

    15 de marzo de 2012 a las 11:22

  • Usar con alloca me parece peligroso si se lanza una excepción, ya que tienes que llamar a los destructores en todos tus objetos.

    – CashCow

    12 de abril de 2013 a las 11:13


1647644594 153 ¿Que usos hay para colocacion nueva
dorcsssc

Jefe 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. Agrupados, C++ llama a esto “instanciación”; pero siempre que la acción del constructor deba invocarse 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 ubicar un objeto C++ global que está anclado a la dirección de un componente de hardware (E/S mapeada en memoria), o para cualquier objeto estático que, por cualquier motivo, deba residir en una dirección fija.

  • artículo interesante, pero no estoy seguro de entender la ventaja de usar esto sobre boost::array. ¿Puedes ampliar eso un poco?

    – GrahamS

    10 de febrero de 2011 a las 11:52

  • boost::array requiere que el tamaño de la matriz sea una constante de tiempo de compilación. Esto no tiene esa limitación.

    – Ferruccio

    10 de febrero de 2011 a las 12:26

  • @Ferruccio Esto es genial, me di cuenta de que su macro es un poco insegura, es decir, el tamaño podría ser una expresión. Si se pasa x+1, por ejemplo, lo expandiría a sizeof(type) * x + 1, lo que sería incorrecto. Necesita poner entre paréntesis su macro para que sea más seguro.

    – Benj

    15 de marzo de 2012 a las 11:22

  • Usar con alloca me parece peligroso si se lanza una excepción, ya que tienes que llamar a los destructores en todos tus objetos.

    – CashCow

    12 de abril de 2013 a las 11:13


En realidad, se requiere implementar cualquier tipo de estructura de datos que asigne más memoria que la mínima requerida para la cantidad de elementos insertados (es decir, cualquier cosa que no sea una estructura vinculada que asigne un nodo a la vez).

Tome contenedores como unordered_map, vectoro deque. Todos estos asignan más memoria de la que se requiere mínimamente para los elementos que ha insertado hasta ahora para evitar requerir una asignación de almacenamiento dinámico para cada inserción. usemos vector como el ejemplo más simple.

Cuando tu lo hagas:

vector<Foo> vec;

// Allocate memory for a thousand Foos:
vec.reserve(1000);

… 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 Foos por todas partes además de tener que invocar a sus destructores incluso para elementos que nunca insertaste 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 a constructores y destructores de izquierda a 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*) lejos de los contenedores que lo utilizan, que construyen elementos manualmente mediante la colocación de elementos nuevos y los destruyen manualmente mediante invocaciones explícitas de destructores.

  • Odio el diseño de std::allocator pero ese es un tema diferente sobre el que evitaré despotricar. 😀

De todos modos, tiendo a usarlo mucho ya que he escrito una serie de contenedores C++ compatibles con el estándar de uso general que no se pudieron construir en términos de los existentes. Entre ellos se incluye una pequeña implementación de vector que construí hace un par de décadas para evitar asignaciones de montón en casos comunes y un trie eficiente en memoria (no asigna un nodo a la vez). En ambos casos, no pude implementarlos usando los contenedores existentes, por lo que tuve que usar placement new para evitar invocar superfluamente constructores y destructores en cosas innecesarias a diestra y siniestra.

Naturalmente, si alguna vez trabaja con asignadores personalizados para asignar objetos individualmente, como una lista libre, generalmente también querrá usar placement newcomo este (ejemplo básico que no se molesta con la seguridad de excepción o RAII):

Foo* foo = new(free_list.allocate()) Foo(...);
...
foo->~Foo();
free_list.free(foo);

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad