Estoy intrigado por cómo funciona el mecanismo de manejo de excepciones de C ++. Específicamente, ¿dónde se almacena el objeto de excepción y cómo se propaga a través de varios ámbitos hasta que se detecta? ¿Está almacenado en algún área global?
Dado que esto podría ser específico del compilador, ¿alguien podría explicar esto en el contexto del conjunto de compiladores g ++?
c++
exception
error-handling
language-implementation
Paul Joseph
fuente
fuente
Respuestas:
Las implementaciones pueden diferir, pero hay algunas ideas básicas que se derivan de los requisitos.
El objeto de excepción en sí mismo es un objeto creado en una función, destruido en un llamador de la misma. Por lo tanto, normalmente no es posible crear el objeto en la pila. Por otro lado, muchos objetos de excepción no son muy grandes. Ergo, uno puede crear, por ejemplo, un búfer de 32 bytes y desbordarse en el montón si realmente se necesita un objeto de excepción más grande.
En cuanto a la transferencia real de control, existen dos estrategias. Uno es registrar suficiente información en la propia pila para desenrollar la pila. Esta es básicamente una lista de destructores para ejecutar y controladores de excepciones que pueden detectar la excepción. Cuando ocurre una excepción, ejecute la pila ejecutando esos destructores hasta que encuentre una captura coincidente.
La segunda estrategia mueve esta información a tablas fuera de la pila. Ahora, cuando ocurre una excepción, la pila de llamadas se usa para averiguar qué ámbitos se ingresan pero no se salen. Luego, se buscan en las tablas estáticas para determinar dónde se manejará la excepción lanzada y qué destructores se ejecutan en el medio. Esto significa que hay menos gastos generales de excepción en la pila; Las direcciones de retorno son necesarias de todos modos. Las tablas son datos adicionales, pero el compilador puede colocarlas en un segmento del programa cargado por demanda.
fuente
free()
o unafclose()
en alguna ruta de código que se usa raramente.Esto se define en 15.1 Lanzar una excepción del estándar.
El lanzamiento crea un objeto temporal.
No se especifica cómo se asigna la memoria para este objeto temporal.
Después de la creación del objeto temporal, el control se pasa al controlador más cercano en la pila de llamadas. desenrollando la pila entre el lanzamiento y el punto de captura. A medida que se desenrolla la pila, las variables de la pila se destruyen en orden inverso al de creación.
A menos que se vuelva a lanzar la excepción, el temporal se destruye al final del controlador donde se detectó.
Nota: Si captura por referencia, la referencia se referirá al temporal. Si captura por valor, el objeto temporal se copia en el valor (y por lo tanto requiere un constructor de copia).
Consejos de S.Meyers (captura por referencia constante).
try { // do stuff } catch(MyException const& x) { } catch(std::exception const& x) { }
fuente
Puede echar un vistazo aquí para obtener una explicación detallada.
También puede ser útil echar un vistazo a un truco utilizado en C simple para implementar algún tipo básico de manejo de excepciones. Esto implica usar setjmp () y longjmp () de la siguiente manera: el primero guarda la pila para marcar el manejador de excepciones (como "catch"), mientras que el segundo se usa para "arrojar" un valor. El valor "arrojado" se ve como si hubiera sido devuelto por una función llamada. El "bloque de prueba" finaliza cuando se llama de nuevo a setjmp () o cuando la función regresa.
fuente
Sé que esta es una pregunta antigua, pero hay una muy buena exposición, que explica los métodos utilizados en cada uno de gcc y VC aquí: http://www.hexblog.com/wp-content/uploads/2012/06/Recon- 2012-Skochinsky-Compiler-Internals.pdf
fuente