2012-01-11 9 views
10

Por favor, considere el siguiente código:¿Qué está sucediendo durante la declaración `delete this;`?

class foo 
{ 
public: 
    foo(){} 
    ~foo(){} 
    void done() { delete this;} 
private: 
    int x; 
}; 

Lo que está pasando (y es válido?) En las dos opciones siguientes:

la opción 1:

void main() 
{ 
    foo* a = new foo(); 
    a->done(); 
    delete a; 
} 

opción 2:

void main() 
{ 
    foo a; 
    a.done(); 
} 

Será la segunda declaración delete a; en opt ion 1 causará una excepción o corrupción de montón?

¿La opción 2 causará una excepción o corrupción de montón?

+0

¿Olvidó por error los corchetes de apertura de la clase o es exactamente el código de la copia pegada? – Neophile

+0

@Nerds: un error tipográfico - corregido ... – NirMH

+0

Interesante. Supongo que el primero causaría una corrupeción segfault o heap, y el segundo hará lo que sea borrar un puntero a la pila. – cha0site

Respuesta

19

delete this; está permitido, elimina el objeto.

Ambos fragmentos de código tienen un comportamiento indefinido: en el primer caso, eliminar un objeto que ya se ha eliminado y, en el segundo caso, eliminar un objeto con duración de almacenamiento automático.

Dado que el comportamiento no está definido, la norma no indica si provocarán una excepción o daños en el montón. Para diferentes implementaciones, puede ser cualquiera de los dos, y puede o no ser el mismo cada vez que ejecutas el código.

+0

Y, con suerte, será el primero. – Xeo

+1

Comportamiento indefinido, la mejor y la respuesta correcta. +1 –

4

Ambos causarían un error.

La primera puntero se suprime dos veces, con el segundo delete que causa el error mientras que la segunda se asigna en la pila y no se puede eliminar implícitamente (error causado por primera invocación de destructor).

+0

¡Me ganaste! – Neophile

+0

Edité la pregunta. Creo que debería asignarse en el montón y luego eliminarse con 'hecho'. ¿Qué está pasando en este caso? Eliminar en la pila no tiene sentido y probablemente OP no le pregunte. – duedl0r

+0

Su respuesta es correcta, pero la pregunta era qué * orden * de error ocurriría. – cha0site

1

Ambos se producirá un error, lo que quiere es:

void main() 
{ 
    foo* a = new foo(); 
    a->done(); 
} 

la que el compilador se expandirá a algo parecido a continuación, que espero marcas borrar "este" un poco menos confuso.

void __foo_done(foo* this) 
{ 
    delete this; 
} 

void main() 
{ 
    foo* a = new foo(); 
    __foo_done(a); 
} 

Véase también, Is it legal (and moral) for a member function to say delete this?

+0

¿Qué sucede si es el primer miembro de un objeto en una clase de diseño estándar (o POD), que se asignó con una versión nueva y que no tiene otros destructores? (No es que eso no sea _insane_, pero, bueno ...) – Random832

+0

@ Random832: No entiendo tu pregunta, lo siento. – ronag

+0

La pregunta frecuente que enlaza proporciona una lista de condiciones bajo las cuales 'eliminar esto' es seguro. Dado que el primer miembro de una clase de diseño estándar tiene la misma dirección que el objeto principal en sí, parece que sería teóricamente otro caso. – Random832

1

Calling delete this es una mala idea. Quien llame al new debe llamar al delete. De ahí los problemas como se destaca en las otras respuestas.

También puede tener fugas de memoria/comportamiento indefinido al construir una matriz de objetos.

+0

¿Por qué llamar 'delete this' es una mala idea? Diría que en muchas aplicaciones (¿la mayoría?), Sería la forma más común de "eliminar". –

+0

@JamesKanze ¿Por qué? 'eliminar esto' no es una forma normal de liberar objetos. –

+0

@VJovic Eso depende de la aplicación.En muchas aplicaciones, el más frecuente (o incluso el único) 'delete's será' delete this'. En otros, el 'delete' estará sistemáticamente en el administrador de transacciones, o algo similar. Y en otros ... Depende de por qué está usando memoria dinámica. Si se trata de objetos modelo, entonces 'delete this' o transaction manager son los más frecuentes. Si es para estructuras de datos complejas de tamaño desconocido, entonces el propietario de la estructura hará el 'delete', y' delete this' casi nunca ocurrirá. –

0

En ambos casos, su pila se dañará. Por supuesto, no puede usar en la opción 2 "->" si el valor de la izquierda (en este caso, "a") no es un puntero, la forma correcta en la opción 2 sería a.done(); Pero sí, debería obtener un montón de corrupción si intenta eliminar 1) lo que ya se ha eliminado, o 2) variable local.

Llamar "eliminar esto" es técnicamente válido y libera la memoria.

EDITAR tenía curiosidad y, de hecho intentado esto:

class foo 
{ 
public: 
    foo(){} 
    ~foo(){} 
    void done() { delete this; } 
    void test() { x = 2; } 
    int getx() { return x; } 
private: 
    int x; 
} ; 


int main(int argc, char* argv[]) 
{ 
    foo *a = new foo(); 
    a->done(); 
    a->test(); 
    int x = a->getx(); 
    printf("%i\n", x); 
    return x; 
} 

Una llamada a printf tendrá éxito, y x contendrá el valor 2.

+0

Gracias - en realidad, el uso "->" en la opción 2 fue un error tipográfico realizado por una edición externa: he actualizado la pregunta nuevamente. – NirMH

+0

Entonces, 'delete this' liberará la memoria del objeto del montón? – NirMH

+0

@NirMH: el uso "->" fue escrito por usted. – duedl0r

0

Sería muy cauteloso sobre el contexto en el que liberas memoria. Mientras se llama "eliminar esto"; intentará liberar la memoria asociada con la estructura de clase, esa operación no tiene ningún medio de deducir el contexto de la asignación de memoria original, es decir, si se asigna desde el montón o la pila. Mi consejo sería volver a trabajar su código para que la operación de desasignación se invoque desde un contexto externo. Tenga en cuenta que esto es diferente de las estructuras de desasignación internas de la clase, en ese caso, use el destructor.

3

Esta pregunta ha sido respondida, pero añadiré un nuevo punto que indica que si su clase llama a eliminar esto, también debe hacer que el destructor sea privado.

Esto asegura que solo la clase puede eliminarse.

Si hace que su destructor sea privado, las dos muestras de código no podrán compilarse.

+0

por supuesto, no previene todos los errores, p. usar el objeto después de llamar a done() en él (incluso al llamar a done() nuevamente invocará una eliminación doble). – CashCow

Cuestiones relacionadas