c++ - usar - ¿Es el código con try-catch-rethrow equivalente al código w/o try-catch?
try catch c++ (6)
Elaboración de salud y salud. - El comentario de Alf :
De http://en.cppreference.com/w/cpp/error/terminate :
std :: terminate () es llamado por el tiempo de ejecución de C ++ cuando falla el manejo de excepciones por cualquiera de las siguientes razones:
1) se lanza una excepción y no se captura ( se define por la implementación si se realiza un desenrollado de pila en este caso )
Así que el desenrollado de la pila podría no suceder si tu
{
// some code, may throw and/or have side effects
}
no está dentro de otro bloque try/catch
.
Ejemplo:
struct A {
A() {}
~A() { std::cout << "~A()" << std::endl; }
};
int main()
{
// try {
A a;
throw 1;
// } catch(...) {
// throw;
// }
}
Bajo el gcc 5.2.0 de Coliru con -O2 no se imprime ~A()
, mientras que con try/catch
se imprime.
UPD : con respecto a su edición sobre unidades de compilación separadas, solo probada con mi gcc 4.8.2 local, el comportamiento es el mismo: no se desenrolla la pila si no hay ningún problema. El ejemplo particular:
ah
:
struct A {
A();
~A();
};
void foo();
a.cpp
:
#include <iostream>
using namespace std;
struct A {
A() {}
~A() { cout << "~A()" << endl; }
};
void foo() {
A a;
throw 1;
}
main.cpp
:
#include "a.h"
int main () {
//try {
foo();
//} catch(...) {
// throw;
//}
}
Creo que si hay una catch
se determina en el tiempo de ejecución, porque de todos modos, cuando se lanza una excepción en el tiempo de ejecución, el programa debe buscar la catch
. Por lo tanto, tiene sentido elegir si desenrollar la pila también en tiempo de ejecución.
¿Bajo qué circunstancias los siguientes dos códigos no son equivalentes?
{
// some code, may throw and/or have side effects
}
try {
// same code as above
} catch(...) {
throw;
}
editar Solo para aclarar, no estoy interesado en (i) las desviaciones del patrón anterior (como más código en el bloque catch) ni en (ii) la intención de invitar a los comentarios sobre el uso adecuado de catch
bloques try
- catch
.
Estoy buscando una respuesta calificada en referencia al estándar de C ++. Esta pregunta fue provocada por un comentario de Cheers y hth. - Alf a esta respuesta mía , declarando sin más explicación que los códigos anteriores no son equivalentes .
De hecho, son diferentes. el desenrollamiento de la pila se realizará en el último, pero no necesariamente en el primero, dependiendo de si se encuentra un controlador de excepciones (algún bloque de catch
más arriba en la pila) en el tiempo de ejecución.
En caso de que atrapes la excepción básica, son completamente iguales. Solo se beneficia al capturar y volver a lanzar una excepción, si hace algo antes de lanzar, como el registro. Pero no deberías atrapar la excepción. Sólo una vez captura excepciones ahora cómo recuperarse.
Los últimos mandan a desenrollar la pila, mientras que en el primero se define por implementación si la pila se desenrolla.
Citas de estándares relevantes (todas de N3337):
[except.ctor]/1:
A medida que el control pasa de una expresión de lanzamiento a un controlador, se invocan los destructores para todos los objetos automáticos construidos desde que se ingresó el bloque try. Los objetos automáticos se destruyen en el orden inverso de la finalización de su construcción.
[except.ctor]/3:
el proceso de llamar a destructores para objetos automáticos construidos en la ruta desde un bloque de prueba a una expresión de lanzamiento se denomina "desenrollado de pila". [...]
[except.terminate]/2:
[Cuando el mecanismo de manejo de excepciones no puede encontrar un manejador para una excepción de lanzamiento], se llama astd::terminate()
(18.8.3). En la situación en la que no se encuentra un controlador coincidente, se define por la implementación si la pila se desenrolla o no antes de llamar astd::terminate()
. [...]
Como tal, si desea garantizar que sus objetos automáticos ejecuten sus destructores en el caso de una excepción no controlada (por ejemplo, algún almacenamiento persistente debe mutarse en la destrucción), try {/*code*/} catch (...) {throw;}
hará eso, pero {/*code*/}
no lo hará.
Se puede realizar una limpieza significativa en el bloque catch antes de volver a lanzar si los recursos no se administran como un idioma RAII
{
// some code, may throw and/or have side effects
}
try {
// same code as above
} catch(...) {
//Some meaningful clean up can be performed here if resources not managed as RAII idiom
throw;
}
Semánticamente eso es equivalente. No estoy seguro, si algunos compiladores podrían ser incapaces de optimizar el try
innecesario - catch
. Prefiero dejar el try
- catch
block out. Eso usualmente hace que el código sea más fácil de entender.
Suponiendo que "algún código" no muestre un comportamiento indefinido (en cuyo caso todas las apuestas están desactivadas, independientemente de si agrega un bloque try / catch o no), no habrá diferencia en el resultado final. La implementación está definida técnicamente (es decir, una implementación debe documentar lo que hace) si se producirá el desenrollado de la pila si nunca se detecta una excepción, pero aún debe haber un informe de cualquier implementación que NO desenrolle la pila en tales circunstancias. Si se produce el desenrollado de la pila, todas las variables locales pasarán fuera del alcance, y aquellas con destructores tendrán invocados los destructores.
Puede haber o no una diferencia mensurable de rendimiento, asociada con la sobrecarga de la configuración antes de que se ejecute "algún código", detectando la excepción (si la hubiera), el reenvío y cualquier limpieza adicional. La diferencia dependerá del compilador y, con los compiladores antiguos, fue potencialmente importante. Con los compiladores modernos, la diferencia de sobrecarga, si la hubiera, sería algo menor, ya que las técnicas de implementación para excepciones y manejo de excepciones han mejorado.