placement new es una forma especial del operador new en C++. Permite colocar un objeto en una dirección específica de una memoria ya asignada. Se utiliza principalmente para el manejo manual de memoria, es la base del funcionamiento de asignadores, el agrupamiento de objetos y la serialización en búferes fijos.
Sintaxis:
#include <new> void* memory = malloc(sizeof(MyClass)); MyClass* obj = new (memory) MyClass(args...); // llama al constructor en la dirección memory
Aquí, la memoria se asigna por separado (por ejemplo, a través de malloc o un asignador), y luego se llama al constructor de la clase en esa memoria. No olvides llamar explícitamente al destructor:
obj->~MyClass(); free(memory);
Ventajas:
¿Se puede utilizar el mismo bloque de memoria para colocar varios objetos consecutivamente con 'placement new', y a qué consecuencias conduce esto?
Una respuesta incorrecta común es que se pueden colocar nuevos objetos en memoria sin preocuparse por el estado anterior. En realidad, si colocas un nuevo objeto en el mismo bloque de memoria encima de uno antiguo sin llamar a su destructor, ocurrirá una fuga de recursos, y llamar al destructor más tarde conducirá a UB.
Ejemplo:
void* place = malloc(sizeof(A)); A* a = new (place) A(); B* b = new (place) B(); // ¡el objeto A no fue destruido! ¡UB!
Historia
En un sistema de alta carga, un desarrollador implementó un grupo de objetos a través de
malloc+placement new, pero olvidó llamar al destructor al devolver un objeto al grupo. Como resultado, los recursos (descriptores del sistema dentro de los objetos) no se liberaron, lo que llevó a fugas y "bloqueo" de los servidores.
Historia
En un proyecto para la serialización de datos binarios se utilizó un búfer y
placement newpara decodificar objetos en su lugar. Sin embargo, olvidaron inicializar la memoria con ceros antes de colocar los objetos, lo que resultó en la lectura de datos no inicializados y errores extraños al trabajar con estructuras POD.
Historia
Uno de los módulos migró a colocar arreglos de objetos utilizando
placement newdentro de un bloque previamente asignado. Después de agregar nuevos miembros dentro de la clase con destructores no triviales, se produjeron cierres inesperados del programa; se olvidó que ahora era necesario llamar explícitamente al destructor para cada elemento durante la limpieza, lo cual no era necesario antes.