colocación nueva y eliminar

¿Cuál es el método correcto para eliminar toda la memoria asignada aquí?

const char* charString = "Hello, World"; void *mem = ::operator new(sizeof(Buffer) + strlen(charString) + 1); Buffer* buf = new(mem) Buffer(strlen(charString)); delete (char*)buf; 

O

  const char* charString = "Hello, World"; void *mem = ::operator new(sizeof(Buffer) + strlen(charString) + 1); Buffer* buf = new(mem) Buffer(strlen(charString)); delete buf; 

o son los dos iguales?

El método correcto es:

 buf->~Buffer(); ::operator delete(mem); 

Solo puede eliminar con el operador de delete lo que recibió del new operador . Si llama directamente al operator new función, también debe llamar directamente a la función de operator delete del operator delete y también debe llamar manualmente al destructor.

Hay dos nociones separadas en C ++:

  1. Los operadores nuevos / borrar.

  2. Nuevas / Eliminar expresiones .

Los operadores asignan y desasignan la memoria. La new expresión construye objetos. La expresión de delete veces destruye un objeto y llama al operador.

¿Por qué “a veces”? Porque depende de la expresión. El new operador nuevo, desnudo y global llama operador-nuevo para asignar memoria y luego construye el objeto; la delete global llama al destructor y desasigna la memoria. Pero todas las otras sobrecargas de new y delete son diferentes:

  • Una nueva expresión sobrecargada llama a un nuevo operador sobrecargado para asignar memoria y luego procede a construir el objeto.
  • Sin embargo, no existe una expresión de eliminación sobrecargada, en particular, no hay “eliminación de ubicación”: en su lugar, debe llamar al destructor manualmente.

Los operadores nuevos / eliminar todavía tienen que estar sobrecargados en los pares coincidentes, porque se llama al operador de eliminación correspondiente cuando un constructor de objetos lanza una excepción. Sin embargo, no hay una forma automática de invocar el destructor para un objeto que ha sido asignado con un new operador sobrecargado, por lo que debe hacerlo usted mismo.

Como primer y más básico ejemplo, considere el operador de colocación-nuevo, que tiene el mandato de tomar el formulario void * operator new (size_t, void * p) throw() { return p; } void * operator new (size_t, void * p) throw() { return p; } . Por lo tanto, el operador de delete coincidencia tiene el mandato de no hacer nada: void operator delete (void *, void *) throw() { } . Uso:

 void * p = ::operator new(5); // allocate only! T * q = new (p) T(); // construct q->~T(); // deconstruct: YOUR responsibility // delete (p) q; <-- does not exist!! It would invoke the following line: ::operator delete(p, q); // does nothing! ::operator delete(q); // deallocate 

Suponiendo que no existe tal cosa como Buffer::operator delete , delete buf; la versión es correcta y hará toda la limpieza apropiada. Para estar un poco más seguro, puedes decir ::delete buf; .

El material de debate entre el abogado y el abogado sigue.

5.3.5 / 1

El operador delete-expression destruye el objeto más derivado (1.8) o el conjunto creado por una nueva expresión .

delete-expression:

  • :: opt delete cast-expression
  • :: opt delete [ ] cast-expression

La primera alternativa es para objetos que no son de matriz, y la segunda es para matrices. …

5.3.5 / 2

… En la primera alternativa ( eliminar objeto ), el valor del operando de delete puede ser un puntero nulo, un puntero a un objeto que no sea de matriz creado por una nueva expresión previa o un puntero a un subobjeto (1.8) representando una clase base de tal objeto (Cláusula 10). Si no, el comportamiento no está definido.

Por lo tanto, el puntero debe apuntar a un objeto creado por una nueva expresión , que se define:

5.3.4 / 1

nueva expresión

  • :: opt new new-placement opt new-type-id _new-initializer_ opt
  • :: opt new new-placement opt ( type-id ) new-initializer opt

nueva ubicación:

  • ( lista de expresión )

Entonces, una “colocación nueva” cuenta como una nueva expresión . Nada que prohíba una expresión de eliminación allí.

Además, resulta que la expresión de eliminación hace exactamente lo correcto para limpiar el objeto a pesar de la creación personalizada.

5.3.5 / 6-9

Si el valor del operando de la expresión de eliminación no es un valor de puntero nulo, la expresión de eliminación invocará el destructor (si lo hay) para el objeto o los elementos de la matriz que se eliminan. …

Si el valor del operando de la expresión de eliminación no es un valor de puntero nulo, la expresión de eliminación llamará a una función de desasignación (3.7.4.2). De lo contrario, no se especifica si se llamará a la función de desasignación. [ Nota: la función de desasignación se llama independientemente de si el destructor para el objeto o algún elemento de la matriz arroja una excepción. – nota final ]

Cuando la palabra clave delete en una expresión-delete está precedida por el operador unary :: , la función de desasignación global se utiliza para desasignar el almacenamiento.

Entonces ::delete buf; es completamente equivalente a:

 try { buf->~Buffer(); } catch(...) { ::operator delete(mem); throw; }