2008-10-21 11 views
43

que esperaba A::~A() a ser llamados en este programa, pero no lo es:¿Por qué no se solicita el destructor en la excepción?

#include <iostream> 

struct A { 
    ~A() { std::cout << "~A()" << std::endl; } 
}; 

void f() { 
    A a; 
    throw "spam"; 
} 

int main() { f(); } 

Sin embargo, si cambio de última línea para

int main() try { f(); } catch (...) { throw; } 

continuación A::~A() es llamado.

Estoy compilando con "Microsoft (R) de 32 bits C/C++ compilador de optimización Versión 14.00.50727.762 de 80x86" de 2005. Estudio de línea de comandos Visual es cl /EHa my.cpp.

¿El compilador está correcto como de costumbre? ¿Qué dice la norma sobre este asunto?

+0

Sólo a título informativo, que reproduce este mismo problema con el mismo código en Visual C++ 2003. +1 para la pregunta. – paercebal

Respuesta

61

El destructor no se está llamando porque se invoca terminate() para la excepción no controlada antes de que la pila se desenrolle.

Los detalles específicos de lo que dice la especificación C++ están fuera de mi conocimiento, pero una traza de depuración con gdb y g ++ parece confirmar esto.

De acuerdo con la sección 15.3 draft standard bala de 9:

 
9 If no matching handler is found in a program, the function terminate() 
    (_except.terminate_) is called. Whether or not the stack is unwound 
    before calling terminate() is implementation-defined. 
2

En el segundo ejemplo, se llama al dtor cuando sale del bloque try {}.

En el primer ejemplo, se llama al dtor ya que el programa se apaga después de salir de la función main() --- en ese momento cout ya puede haber sido destruido.

+0

No, esto está mal. Se garantiza que se llamará al destructor antes de que el programa abandone 'main'. El destructor 'cout' se garantiza que se llamará * después *. –

+0

... corrección: ¡el destructor de 'a' debe invocarse antes de dejar' f'! –

+0

Además, el programa nunca deja main en el primer ejemplo. Se cancela a través de terminar con una "excepción inesperada". – ejgottl

2

Supuse también que el compilador no genera el código relativo a "a", ya que no está referenciado, pero aún así, no es el comportamiento correcto ya que el destructor hace algo que debe ejecutarse.

Por lo tanto, probé en VS2008/vc9 (+ SP1), Depurar y liberar y ~ A se llama después de que se lanza la excepción, saliendo de f() - ese es el comportamiento correcto, si estoy en lo cierto.

Ahora probé con VS2005/vc8 (+ SP1) y es el mismo comportamiento.

Utilicé puntos de interrupción para estar seguro. Acabo de consultar con la consola y también tengo el mensaje "~ A". Tal vez lo hiciste mal en otro lugar?

+0

Creé un archivo de texto con el primer ejemplo (sin intentar), abrí el "Símbolo del sistema de Visual Studio 2005" y compilé el archivo con 'cl/EHa my.cpp'. Resultado de la ejecución: Esta aplicación ha solicitado al Runtime que lo finalice de forma inusual. Póngase en contacto con el equipo de soporte de la aplicación para obtener más información. – Constantin

+0

No estoy familiarizado con los parámetros de compilación en la línea de comandos, pero supongo que es similar al modo Release, donde si no hay try/catch el código no irá más allá de la instrucción throw (eso es lo que sucede cuando lo intento) y la aplicación simplemente se "colgará" (es decir, el comportamiento deseado) – Klaim

+0

Después de consultar las listas de ensamblaje, creo que las optimizaciones están desactivadas de manera predeterminada. Por lo tanto, está más cerca de Debug. Por cierto, vea el comentario de paercebal, logró reproducir con VS2003. – Constantin

1

Lo siento, no tienen una copia de la norma en mí.
lo haría sin duda como una respuesta definitiva a esta, por lo que alguien con copia de la norma quiere compartir con pelos y señales sobre qué está sucediendo:

Desde mi entender interrupción se sólo se llama si y sólo si:

  • La excepción el mecanismo de manejo no puede encontrar un controlador para una excepción lanzada.
    Los siguientes son los casos más específicas de esta:
    • Durante pila de desenrollado, una excepción escapa un destructor.
    • Una expresión arrojada, una excepción se escapa del constructor.
    • Se escapa una excepción del constructor/destructor de una estática no local (es decir, global)
    • Una excepción escapa a una función registrada con atexit().
    • Una excepción escapa principal()
  • Tratando de volver a lanzar una excepción cuando no es la excepción se propaga actualmente.
  • una excepción inesperada escapa una función con especificadores de excepción (a través inesperado)
13

lenguaje C++ estados especificación: El proceso de llamar a los destructores de objetos automáticos construidos en el camino de un bloque try a una banda expresión se llama "desenrollado de la pila". Su código original no contiene el bloque try, por eso no se desenrolla la pila.

2

Esta pregunta es fácil de hacer, así que comparto mi situación aquí.

Asegúrese excepcion yor no atraviesa extern "C" límite o utilizar MSVC opción/EHS (Habilitar C Excepciones ++ = Sí con funciones Externo C (/ EHS))

Cuestiones relacionadas