2009-07-27 19 views
10

Sé que I arroja excepciones desde un destructor.Destructor que llama a una función que puede lanzar una excepción en C++

Si mi destructor llama a una función que puede arrojar una excepción, ¿está bien si la atrapo en el destructor y no la arrojo más? ¿O puede causar aborto de todos modos y no debería llamar a tales funciones de un destructor en absoluto?

+0

Solo para aclararlo, ¿estás preguntando si está bien si el destructor detecta la excepción, por lo que nunca abandona el destructor, o si está bien dejar que abandone el destructor siempre que está atrapado afuera? – jalf

+0

Pregunto si está bien si la excepción permanece en el D'tor. –

Respuesta

19

Sí, eso es legal. Una excepción no debe ser escape desde el destructor, pero pase lo que pase dentro del destructor, o en las funciones que llama, depende de usted.

(Técnicamente, una excepción puede escapar de una llamada al destructor también. Si esto sucede durante el desenrollado de pila porque otra excepción fue arrojado, std::terminate se llama. Así que está bien definido por la norma, pero es un realmente mala idea.)

+2

Perdón por nitpicking, pero usaría un término que no sea "legal". Lanzar una excepción en el destructor también es "legal", i. mi. se compilará y se ejecutará. Pero es una mala práctica que causará efectos desagradables. – Dima

+0

Sí y no. Tienes razón, es técnicamente legal lanzar una excepción desde un destructor (se llama "std :: terminate" en ese momento). Pero su definición de "legal" es incorrecta. El hecho de que algo compila y ejecuta de ninguna manera lo hace legal C++. – jalf

+1

No estoy seguro de lo que quiere decir legal, pero estoy con Dima en este caso.Aunque C++ no utiliza el trabajo legal, define un * programa bien formado * (probablemente el más cercano a lo que considero 'legal'), * comportamiento no especificado * y * comportamiento indefinido *. Permitir que una excepción salga de un destructor puede ocurrir en un programa bien formado y el comportamiento no está indefinido ni no especificado. Eso no impide que sea un comportamiento casi universalmente indeseable. –

-1

Puede encontrar this page de C++ FAQ Lite para ser informativo. La respuesta básica es "no lo hagas". ¿Dónde planea exactamente atrapar esta excepción? Lo que sea que planee hacer cuando detecte esa excepción, simplemente haga una llamada a la función en su lugar o algo así (por ejemplo, inicie sesión o configure una bandera para advertir al usuario o lo que sea). Lanzar excepciones desde el destructor corre el riesgo de causar la terminación de todo su programa.

+0

¿Qué importancia tiene esto para la pregunta? Si llama a una función que puede lanzar std :: bad_alloc, ¿cuál sería la alternativa? Escribir una función de "envoltura"? Esa no es una solución, eso solo oculta la excepción en otra capa. – MSalters

0

Respuesta simple, nunca permita una excepción de un dtor!

La respuesta complicada. Solo se obtiene realmente clavado si la excepción escapa del dtor mientras hay otra excepción activa. El caso normal para esto es cuando ya está desenrollando la pila de otra excepción y el objeto en cuestión se destruye. En ese caso, si la excepción se escapa del dtor, se llama al std::terminate, tenga en cuenta que puede poner su propio controlador para std::terminate llamando al std::set_terminate. La implementación predeterminada de std::terminate es llamar a abortar.

Para complicar las cosas más mayoría de las funciones, que quieren hacer ninguna garantía sobre su seguridad excepción, sobre todo la garantía básica o la garantía fuerte, dependen de los tipos subyacentes a sí mismos no lanzar en su dtor *

La verdadera La pregunta es, ¿en qué estado estaría su programa cuando ocurra este error? ¿Cómo te puedes recuperar? ¿Dónde debe manejarse esta recuperación? Necesita ver su caso específico y resolver estos problemas. A veces está bien atrapar la excepción e ignorarla. Otras veces necesita levantar algunas banderas rojas.

Así que la respuesta es: permitió C++ lanzar una excepción en un dtor, pero nunca deberías permitir que escapara.

* He aquí una breve synopsis de las garantías de excepción (aquí hay un mucho más tiempo article)

  1. Resumen: definir brevemente las garantías Abrahams seguridad excepción (básico, fuertes, y nothrow).

La garantía básica es que fallaron operaciones pueden alterar el estado del programa, pero no se producen fugas y afectados objetos/módulos son todavía destructible y utilizable, en un estado coherente (pero no necesariamente predecible).

El fuerte garantía implica transaccional commit/rollback semántica: No se pudo operaciones de garantía estado del programa no se ha modificado con respecto a los objetos intervenidos. Esto significa que no hay efectos secundarios que afecten a los objetos , incluida la validez o contenidos de los objetos auxiliares relacionados , como los iteradores que apuntan a los contenedores que se están manipulando.

La garantía nohrow significa que las operaciones fallidas de no sucederán. La operación no lanzará una excepción.

+0

como entiendo la pregunta, él no está preguntando si las excepciones dejan al dtor en absoluto, sino simplemente si está bien que el dtor llame a una función que arroje una excepción, siempre que el dtor capte la excepción para que no se propague – jalf

3

Sí.

Observe la clase std :: fstream en la biblioteca estándar para ver un ejemplo.

  • close() podría lanzar una excepción.
  • El destructor puede llamar a close() pero el destructor no tira (se tragará cualquier excepción).

El concepto es que si el destructor llama a cualquier método que pueda lanzar, estos métodos deberían ser públicos. Por lo tanto, si el usuario de su objeto desea verificar excepciones, puede usar los métodos públicos y manejar la excepción. Si no les importa la excepción, simplemente deja que el destructor maneje el problema.

Volviendo al ejemplo std :: fstream.

{ 
    std::fstream text("Plop"); 
    // Load Text. 

    // I don't care if the close fails. 
    // So let the destructor handle it and discard exceptions 
} 



{ 
    // If this fails to write I should at least warn the user. 
    // So in this case I will explicitly try and close it. 
    try 
    { 
     std::ofstram password("/etc/password"); 
     // Update the password file. 

     password.close(); 
    } 
    catch(...) 
    { 
      Message.ShowDialog("You failed to update the Password File"); 
    } 
} 
1

Usted puede encontrar algunos ejemplos aquí: https://software.intel.com/sites/products/documentation/doclib/iss/2013/sa-ptr/sa-ptr_win_lin/GUID-D2983B74-74E9-4868-90E0-D65A80F8F69F.htm

Si una excepción deja destructor durante el desenrollado pila de otra excepción que se propagan, a continuación, std :: terminar() es llamado.

Cuando no hay desenrollado de la pila en curso, una excepción puede dejar al destructor sin que se llame a std :: terminate(). Sin embargo, para los objetos asignados en el montón esto dará como resultado la pérdida de memoria porque no se llamará "delete operador" para el objeto que arroja la excepción de su destructor. Sorprendentemente, el destructor de la clase base aún se llama en este caso: What happens to base class destructor if a derived class destructor throws an exception

Si la excepción queda atrapada dentro del destructor (para que la excepción no abandone el destructor), entonces no hay problema incluso si se desenrolla la pila de otra excepción en progreso. Este caso se describe más detalladamente aquí: http://bin-login.name/ftp/pub/docs/programming_languages/cpp/cffective_cpp/MEC/MI11_FR.HTM

Cuestiones relacionadas