¿Está permitido delete this;
si la instrucción delete es la última instrucción que se ejecutará en esa instancia de la clase? Por supuesto, estoy seguro de que el objeto representado por el this
puntero está new
creado.
Estoy pensando en algo como esto:
void SomeModule::doStuff()
{
// in the controller, "this" object of SomeModule is the "current module"
// now, if I want to switch over to a new Module, eg:
controller->setWorkingModule(new OtherModule());
// since the new "OtherModule" object will take the lead,
// I want to get rid of this "SomeModule" object:
delete this;
}
¿Puedo hacer esto?
c++
memory-management
new-operator
delete-operator
self-destruction
Martijn Courteaux
fuente
fuente
delete this
ha creado un acoplamiento estrecho entre la clase y el método de asignación utilizado para crear objetos de esa clase. Ese es un diseño de OO muy pobre, ya que lo más fundamental en OOP es hacer clases autónomas que no saben o no se preocupan por lo que está haciendo la persona que llama. Por lo tanto, una clase diseñada adecuadamente no debería saber o importar cómo se asignó. Si por alguna razón necesita un mecanismo tan peculiar, creo que un mejor diseño sería usar una clase de envoltura alrededor de la clase real y dejar que la envoltura se encargue de la asignación.setWorkingModule
?Respuestas:
C ++ FAQ Lite tiene una entrada específica para esto
Creo que esta cita lo resume muy bien
fuente
Sí,
delete this;
tiene resultados definidos, siempre y cuando (como haya notado) se asegure de que el objeto se asignó dinámicamente y (por supuesto) nunca intente usar el objeto después de que se destruya. A lo largo de los años, se han formulado muchas preguntas sobre lo que dice el estándar específicamentedelete this;
, en lugar de eliminar algún otro puntero. La respuesta a eso es bastante corta y simple: no dice mucho de nada. Simplemente dice quedelete
el operando debe ser una expresión que designe un puntero a un objeto, o una matriz de objetos. Entra en bastante detalle sobre cosas como cómo se da cuenta de qué función de desasignación (si la hay) llamar para liberar la memoria, pero toda la sección sobredelete
(§ [expr.delete]) no mencionadelete this;
específicamente nada. La sección sobre destructores mencionadelete this
en un lugar (§ [class.dtor] / 13):Eso tiende a respaldar la idea de que el estándar considera
delete this;
válido: si no fuera válido, su tipo no sería significativo. Ese es el único lugar que el estándar mencionadelete this;
, hasta donde yo sé.De todos modos, algunos consideran
delete this
un truco desagradable y le dicen a cualquiera que escuche que debe evitarse. Un problema comúnmente citado es la dificultad de garantizar que los objetos de la clase solo se asignen dinámicamente. Otros lo consideran un idioma perfectamente razonable, y lo usan todo el tiempo. Personalmente, estoy en un punto intermedio: rara vez lo uso, pero no dudes en hacerlo cuando parezca ser la herramienta adecuada para el trabajo.La primera vez que usa esta técnica es con un objeto que tiene una vida que es casi completamente propia. Un ejemplo que James Kanze citó fue un sistema de facturación / seguimiento en el que trabajó para una compañía telefónica. Cuando empiezas a hacer una llamada telefónica, algo toma nota de eso y crea un
phone_call
objeto. A partir de ese momento, elphone_call
objeto maneja los detalles de la llamada telefónica (hacer una conexión cuando marca, agregar una entrada a la base de datos para decir cuándo comenzó la llamada, posiblemente conectar a más personas si hace una llamada de conferencia, etc.) Cuando las últimas personas en la llamada cuelgan, elphone_call
objeto realiza su contabilidad final (por ejemplo, agrega una entrada a la base de datos para decir cuándo colgó, para que puedan calcular cuánto tiempo duró su llamada) y luego se destruye a sí mismo. La vida de laphone_call
El objeto se basa en cuándo la primera persona inicia la llamada y cuándo las últimas personas abandonan la llamada, desde el punto de vista del resto del sistema, es básicamente completamente arbitrario, por lo que no puede vincularlo a ningún ámbito léxico en el código , o cualquier cosa en ese orden.Para cualquier persona a la que le importe cuán confiable puede ser este tipo de codificación: si realiza una llamada telefónica a, desde o a través de casi cualquier parte de Europa, hay muchas posibilidades de que se maneje (al menos en parte) mediante código eso hace exactamente esto.
fuente
bool selfDelete
parámetro en el constructor que se asigna a una variable miembro. De acuerdo, esto significa entregarle al programador suficiente cuerda para atar una soga, pero creo que eso es preferible a las pérdidas de memoria.this
. Sí, el código está siendo manejado por exactamentethis
. ;)Si te asusta, hay un truco perfectamente legal:
Sin
delete this
embargo, creo que es idiomático C ++, y solo lo presento como una curiosidad.Hay un caso en el que esta construcción es realmente útil: puede eliminar el objeto después de lanzar una excepción que necesita datos del miembro del objeto. El objeto permanece válido hasta después del lanzamiento.
Nota: si está utilizando un compilador anterior a C ++ 11 que puede usar en
std::auto_ptr
lugar destd::unique_ptr
, hará lo mismo.fuente
unique_ptr
desde otrounique_ptr
requiere un movimiento, pero no desde un puntero sin formato. ¿A menos que las cosas cambien en C ++ 17?Una de las razones por las que C ++ fue diseñado fue para facilitar la reutilización del código. En general, C ++ debe escribirse para que funcione si la clase se instancia en el montón, en una matriz o en la pila. "Eliminar esto" es una práctica de codificación muy mala porque solo funcionará si se define una única instancia en el montón; y es mejor que no haya otra declaración de eliminación, que generalmente utilizan la mayoría de los desarrolladores para limpiar el montón. Hacer esto también supone que ningún programador de mantenimiento en el futuro curará una pérdida de memoria falsamente percibida al agregar una declaración de eliminación.
Incluso si sabe de antemano que su plan actual es asignar solo una instancia en el montón, ¿qué pasa si algún desarrollador feliz aparece en el futuro y decide crear una instancia en la pila? O, ¿qué pasa si corta y pega ciertas partes de la clase a una nueva clase que tiene la intención de usar en la pila? Cuando el código llegue a "eliminar esto", se apagará y lo eliminará, pero luego, cuando el objeto salga del alcance, llamará al destructor. Luego, el destructor intentará eliminarlo nuevamente y luego se le aplicará una manguera. En el pasado, hacer algo así arruinaría no solo el programa sino también el sistema operativo y la computadora. En cualquier caso, esto NO es muy recomendable y casi siempre debe evitarse. Tendría que estar desesperado, seriamente enyesado,
fuente
Está permitido (simplemente no use el objeto después de eso), pero no escribiría ese código en la práctica. Creo que
delete this
debería aparecer sólo en las funciones que llamarelease
oRelease
y se parece a:void release() { ref--; if (ref<1) delete this; }
.fuente
Bueno, en la
delete this
construcción del Modelo de objetos componentes (COM) puede ser una parte delRelease
método que se llama cada vez que desea liberar un objeto requerido:fuente
Este es el idioma central para los objetos contados por referencia.
El recuento de referencias es una forma sólida de recolección de basura determinista: garantiza que los objetos administren su PROPIA vida útil en lugar de depender de punteros 'inteligentes', etc. para hacerlo por ellos. Solo se accede al objeto subyacente a través de punteros inteligentes "Referencia", diseñados para que los punteros incrementen y disminuyan un entero miembro (el recuento de referencia) en el objeto real.
Cuando la última referencia cae de la pila o se elimina, el recuento de referencia irá a cero. El comportamiento predeterminado de su objeto será una llamada para "eliminar esto" a la recolección de basura: las bibliotecas que escribo proporcionan una llamada virtual protegida "CountIsZero" en la clase base para que pueda anular este comportamiento para cosas como el almacenamiento en caché.
La clave para hacer esto seguro es no permitir a los usuarios acceder al CONSTRUCTOR del objeto en cuestión (protegerlo), sino hacer que llamen a algún miembro estático, la FÁBRICA, como "Creación de referencia estática (...)". De esa manera, SABE con certeza que siempre están construidos con "nuevo" ordinario y que no hay ningún puntero sin formato disponible, por lo que "eliminar esto" nunca explotará.
fuente
Puedes hacerlo Sin embargo, no puede asignar a esto. Por lo tanto, la razón por la que declaras hacer esto, "Quiero cambiar la vista", parece muy cuestionable. El mejor método, en mi opinión, sería que el objeto que contiene la vista reemplace esa vista.
Por supuesto, está utilizando objetos RAII y, por lo tanto, no necesita llamar a eliminar en absoluto ... ¿verdad?
fuente
Esta es una vieja pregunta contestada, pero @Alexandre preguntó "¿Por qué alguien querría hacer esto?", Y pensé que podría proporcionar un ejemplo de uso que estoy considerando esta tarde.
Código heredado Utiliza punteros desnudos Obj * obj con un obj de eliminación al final.
Lamentablemente, a veces necesito mantener el objeto vivo durante más tiempo.
Estoy considerando convertirlo en un puntero inteligente de referencia. Pero habría mucho código para cambiar, si tuviera que usar
ref_cnt_ptr<Obj>
todas partes. Y si mezcla Obj * desnudo y ref_cnt_ptr, puede eliminar el objeto implícitamente cuando desaparezca el último ref_cnt_ptr, aunque todavía haya Obj * vivo.Así que estoy pensando en crear un explícito_delete_ref_cnt_ptr. Es decir, un puntero contado de referencia donde la eliminación solo se realiza en una rutina de eliminación explícita. Utilizándolo en el único lugar donde el código existente conoce la vida útil del objeto, así como en mi nuevo código que mantiene el objeto vivo por más tiempo.
Aumentando y decrementando el recuento de referencias a medida que se manipula explícitamente_delete_ref_cnt_ptr.
Pero NO se libera cuando el recuento de referencias se ve como cero en el destructor explicit_delete_ref_cnt_ptr.
Solo se libera cuando se considera que el recuento de referencia es cero en una operación explícita de eliminación. Por ejemplo, en algo como:
De acuerdo, algo así. Es un poco inusual tener un tipo de puntero contado de referencia que no elimine automáticamente el objeto señalado en el destructor ptr rc'ed. Pero parece que esto podría hacer que mezclar punteros desnudos y punteros rojos sea un poco más seguro.
Pero hasta ahora no es necesario eliminar esto.
Pero entonces se me ocurrió: si el objeto apuntado, el puntero, sabe que se está contando por referencia, por ejemplo, si el recuento está dentro del objeto (o en alguna otra tabla), entonces la rutina delete_if_rc0 podría ser un método de objeto de puntero, no el puntero (inteligente).
En realidad, no necesita ser un método miembro, pero podría ser una función libre:
(Por cierto, sé que el código no es del todo correcto; se vuelve menos legible si agrego todos los detalles, así que lo dejo así).
fuente
Eliminar esto es legal siempre que el objeto esté en el montón. Debería requerir que el objeto sea solo un montón. La única forma de hacerlo es proteger el destructor; de esta manera, eliminar se puede llamar SOLO desde la clase, por lo que necesitaría un método que garantice la eliminación
fuente