c++ placement-new

c++ - ¿Es seguro llamar a la ubicación nueva en ''this'' para un objeto trivial?



placement-new (2)

Sé que esta pregunta ya se hizo varias veces, pero no pude encontrar una respuesta para este caso en particular.

Digamos que tengo una clase trivial que no posee ningún recurso y tiene un destructor vacío y un constructor predeterminado. Tiene un puñado de variables miembro con inicialización en clase; ninguno de ellos es const .

Quiero reinicializar y objetar tal clase sin escribir a deInit método deInit . ¿Es seguro hacerlo así?

void A::deInit() { new (this)A{}; }

No puedo ver ningún problema con él: el objeto siempre está en estado válido, this todavía apunta a la misma dirección; pero es C ++, así que quiero estar seguro.


De manera similar a la legalidad de delete this , la ubicación nueva para this también está permitida hasta donde yo sé. Además, con respecto a si this u otros punteros / referencias preexistentes se pueden usar después, hay algunas restricciones:

[basic.life]

Si, después de que la vida útil de un objeto ha finalizado y antes del almacenamiento que el objeto ocupado se reutiliza o se libera, se crea un nuevo objeto en la ubicación de almacenamiento que ocupó el objeto original, un puntero que apuntaba al objeto original, una referencia que referido al objeto original, o el nombre del objeto original se referirá automáticamente al nuevo objeto y, una vez que ha comenzado la vida útil del nuevo objeto, puede usarse para manipular el nuevo objeto, si:

  • el almacenamiento para el nuevo objeto se superpone exactamente a la ubicación de almacenamiento que ocupaba el objeto original, y
  • el nuevo objeto es del mismo tipo que el objeto original (ignorando los calificadores cv de nivel superior), y
  • el tipo del objeto original no está calificado const y, si es un tipo de clase, no contiene ningún miembro de datos no estático cuyo tipo esté calificado const o un tipo de referencia, y
  • ni el objeto original ni el nuevo objeto es un subobjeto potencialmente superpuesto ([intro.object]).

Los dos primeros están satisfechos en este ejemplo, pero los dos últimos deberán tenerse en cuenta.

Con respecto al tercer punto, dado que la función no está calificada para const, debería ser bastante seguro asumir que el objeto original no es const. La falla está en el lado de la persona que llama si la constidad ha sido eliminada. Con respecto al miembro const / reference, creo que puede verificarse afirmando que esto es asignable:

static_assert(std::is_trivial_v<A> && std::is_copy_assignable_v<A>);

Por supuesto, dado que la asignabilidad es un requisito, en su lugar, simplemente puede usar *this = {}; que esperaría producir el mismo programa. Un caso de uso quizás más interesante podría ser reutilizar la memoria de *this para un objeto de otro tipo (lo que no cumpliría los requisitos para usar this , al menos sin reinterpretar + lavar).

Similar a delete this , la ubicación nueva para this difícilmente podría describirse como "segura".


Las reglas que cubren esto están en [basic.life]/5

Un programa puede finalizar la vida útil de cualquier objeto reutilizando el almacenamiento que ocupa el objeto o llamando explícitamente al destructor para un objeto de un tipo de clase. Para un objeto de un tipo de clase, no se requiere que el programa llame al destructor explícitamente antes de que el almacenamiento que ocupa el objeto sea reutilizado o liberado; sin embargo, si no hay una llamada explícita al destructor o si no se usa una expresión de eliminación para liberar el almacenamiento, el destructor no se llama implícitamente y cualquier programa que dependa de los efectos secundarios producidos por el destructor tiene un comportamiento indefinido.

y [basic.life]/8

Si, después de que la vida útil de un objeto ha finalizado y antes del almacenamiento que el objeto ocupado se reutiliza o se libera, se crea un nuevo objeto en la ubicación de almacenamiento que ocupó el objeto original, un puntero que apuntaba al objeto original, una referencia que referido al objeto original, o el nombre del objeto original se referirá automáticamente al nuevo objeto y, una vez que ha comenzado la vida útil del nuevo objeto, puede usarse para manipular el nuevo objeto, si:

  • el almacenamiento para el nuevo objeto se superpone exactamente a la ubicación de almacenamiento que ocupaba el objeto original, y

  • el nuevo objeto es del mismo tipo que el objeto original (ignorando los calificadores cv de nivel superior), y

  • el tipo del objeto original no está calificado const y, si es un tipo de clase, no contiene ningún miembro de datos no estático cuyo tipo esté calificado const o un tipo de referencia, y

  • ni el objeto original ni el nuevo objeto es un subobjeto potencialmente superpuesto ([intro.object]).

Dado que su objeto es trivial, no tiene que preocuparse por [basic.life] / 5 y siempre y cuando satisfaga los puntos de bala de [basic.life] / 8, entonces es seguro.