tipo - ¿Debo usar un especificador de excepciones en C++?
try catch ejemplo (14)
Creo que el estándar excepto la convención (para C ++)
Los especificadores de excepciones fueron un experimento en el estándar de C ++ que falló principalmente.
La excepción es que el especificador no throw es útil, pero también debe agregar el bloque try catch apropiado internamente para asegurarse de que el código coincida con el especificador. Herb Sutter tiene una página sobre el tema. Gotch 82
Además, creo que vale la pena describir las garantías de excepción.
Básicamente se trata de documentación sobre cómo el estado de un objeto se ve afectado por las excepciones que escapan de un método en ese objeto. Lamentablemente, no son aplicadas o mencionadas por el compilador.
Impulso y excepciones
Garantías de excepción
Sin garantía:
No hay garantía sobre el estado del objeto después de que una excepción escapa a un método
En estas situaciones, el objeto ya no debería ser utilizado.
Garantía Básica:
En casi todas las situaciones, esta debería ser la garantía mínima que proporciona un método.
Esto garantiza que el estado del objeto está bien definido y aún se puede usar de forma consistente.
Garantía fuerte: (también conocida como Garantía transaccional)
Esto garantiza que el método se completará con éxito
O se lanzará una excepción y el estado de los objetos no cambiará.
Sin garantía de tiro:
El método garantiza que no se permite la propagación de excepciones del método.
Todos los destructores deberían hacer esta garantía.
| NB Si una excepción se escapa de un destructor mientras una excepción ya se está propagando
| la aplicación terminará
En C ++, puede especificar que una función puede lanzar o no una excepción mediante el uso de un especificador de excepciones. Por ejemplo:
void foo() throw(); // guaranteed not to throw an exception
void bar() throw(int); // may throw an exception of type int
void baz() throw(...); // may throw an exception of some unspecified type
Tengo dudas sobre su uso debido a lo siguiente:
- El compilador realmente no aplica los especificadores de excepciones de ninguna manera rigurosa, por lo que los beneficios no son grandes. Idealmente, le gustaría obtener un error de compilación.
- Si una función viola un especificador de excepciones, creo que el comportamiento estándar es terminar el programa.
- En VS.Net, trata a throw (X) como throw (...), por lo que el cumplimiento del estándar no es fuerte.
¿Crees que se deben usar especificadores de excepciones?
Responda "sí" o "no" y proporcione algunos motivos para justificar su respuesta.
Del artículo:
http://www.boost.org/community/exception_safety.html
"Es bien sabido que es imposible escribir un contenedor genérico a salvo de excepciones". Este reclamo se suele escuchar con referencia a un artículo de Tom Cargill [4] en el que explora el problema de la seguridad de excepciones para una plantilla de pila genérica. En su artículo, Cargill plantea muchas preguntas útiles, pero lamentablemente no presenta una solución a su problema.1 Concluye sugiriendo que una solución puede no ser posible. Desafortunadamente, su artículo fue leído por muchos como "prueba" de esa especulación. Desde su publicación, ha habido muchos ejemplos de componentes genéricos de excepción segura, entre ellos los contenedores de la biblioteca estándar de C ++.
Y, de hecho, puedo pensar en formas de hacer que las excepciones de las clases de plantillas sean seguras. A menos que no tenga control sobre todas las subclases, entonces puede tener un problema de todos modos. Para hacer esto uno podría crear typedefs en sus clases que definen las excepciones lanzadas por varias clases de plantilla. Este piensa que el problema es como siempre virar después en lugar de diseñarlo desde el principio, y creo que es esta sobrecarga el verdadero obstáculo.
El único especificador de excepción útil es "throw ()", como en "does not throw".
En general, no usaría especificadores de excepciones. Sin embargo, en los casos en que si hubiera alguna otra excepción proveniente de la función en cuestión que el programa definitivamente no podría corregir , entonces puede ser útil. En todos los casos, asegúrese de documentar claramente qué excepciones podrían esperarse de esa función.
Sí, el comportamiento esperado de una excepción no especificada lanzada desde una función con especificadores de excepción es llamar a terminate ().
También notaré que Scott Meyers aborda este tema en C ++ más efectivo. Su C ++ efectivo y C ++ más efectivo son libros altamente recomendados.
Evite las especificaciones de excepción en C ++. Las razones que da en su pregunta son un buen comienzo de por qué.
Vea "Una mirada pragmática a las especificaciones de excepción" de Herb Sutter.
Excepción de especificaciones = basura, pregunte a cualquier desarrollador de Java mayor de 30 años
Las especificaciones de excepciones no son herramientas maravillosamente útiles en C ++. Sin embargo, existe / un / buen uso para ellos, si se combina con std :: inesperado.
Lo que hago en algunos proyectos es código con especificaciones de excepción, y luego invoco set_unexpected () con una función que generará una excepción especial de mi propio diseño. Esta excepción, en la construcción, obtiene una traza inversa (en una plataforma específica) y se deriva de std :: bad_exception (para permitir que se propague si se desea). Si causa una llamada a terminate (), como suele suceder, la traza inversa se imprime con () (así como la excepción original que la causó, no es difícil de encontrar) y así obtengo información de dónde estaba mi contrato. violado, como qué excepción de biblioteca inesperada se lanzó.
Si hago esto, nunca permitiré la propagación de las excepciones de la biblioteca (excepto las estándar) y obtendré todas mis excepciones de std :: exception. Si una biblioteca decide tirar, atraparé y convertiré en mi propia jerarquía, lo que me permitirá controlar siempre el código. Las funciones con plantilla que llaman a funciones dependientes deben evitar las especificaciones de excepción por razones obvias; pero es raro tener una interfaz de función de plantilla con el código de la biblioteca de todos modos (y pocas bibliotecas realmente usan plantillas de una manera útil).
No.
Aquí hay varios ejemplos de por qué:
El código de la plantilla es imposible de escribir con especificaciones de excepción,
template<class T> void f( T k ) { T x( k ); x.x(); }
Las copias pueden arrojar, el parámetro que pasa puede arrojar,
x()
puede arrojar alguna excepción desconocida.Las especificaciones de excepción tienden a prohibir la extensibilidad.
virtual void open() throw( FileNotFound );
podría evolucionar hacia
virtual void open() throw( FileNotFound, SocketNotReady, InterprocessObjectNotImplemented, HardwareUnresponsive );
Realmente podrías escribir eso como
throw( ... )
El primero no es extensible, el segundo es demasiado ambicioso y el tercero es realmente lo que quieres decir cuando escribes funciones virtuales.
Código heredado
Cuando escribe código que se basa en otra biblioteca, realmente no sabe lo que podría hacer cuando algo sale mal.
int lib_f(); void g() throw( k_too_small_exception ) { int k = lib_f(); if( k < 0 ) throw k_too_small_exception(); }
g
terminará, cuandolib_f()
lanza. Esto es (en la mayoría de los casos) no lo que realmente quieres.std::terminate()
nunca debería ser llamado. Siempre es mejor dejar que la aplicación se cuelgue con una excepción no controlada, desde la cual puede recuperar un seguimiento de pila, que morir en silencio / violentamente.Escribir código que devuelve errores comunes y arroja en ocasiones excepcionales.
Error e = open( "bla.txt" ); if( e == FileNotFound ) MessageUser( "File bla.txt not found" ); if( e == AccessDenied ) MessageUser( "Failed to open bla.txt, because we don''t have read rights ..." ); if( e != Success ) MessageUser( "Failed due to some other error, error code = " + itoa( e ) ); try { std::vector<TObj> k( 1000 ); // ... } catch( const bad_alloc& b ) { MessageUser( "out of memory, exiting process" ); throw; }
Sin embargo, cuando su biblioteca solo arroja sus propias excepciones, puede usar especificaciones de excepción para expresar su intención.
No. Si los usa y se lanza una excepción que no especificó, ya sea por su código o código llamado por su código, entonces el comportamiento predeterminado es terminar su programa inmediatamente.
Además, creo que su uso ha quedado obsoleto en los borradores actuales del estándar C ++ 0x.
Pueden ser útiles para las pruebas unitarias, de modo que cuando se escriben las pruebas se sabe qué esperar que arroje la función cuando falla, pero no existe una aplicación que las rodee en el compilador. Creo que son códigos adicionales que no son necesarios en C ++. Cualquiera que elija todo lo que debe estar seguro es que sigue el mismo estándar de codificación en todo el proyecto y los miembros del equipo para que su código siga siendo legible.
Sí, si le interesa la documentación interna. O tal vez escribir una biblioteca que otros usarán, para que puedan decir lo que sucede sin consultar la documentación. Lanzar o no tirar se puede considerar parte de la API, casi como el valor de retorno.
Estoy de acuerdo, no son realmente útiles para aplicar el estilo correcto de Java en el compilador, pero es mejor que nada o comentarios caóticos.
Si está escribiendo un código que será utilizado por personas que preferirían ver la declaración de la función que cualquier comentario a su alrededor, una especificación les dirá qué excepciones pueden querer detectar.
De lo contrario, no me parece particularmente útil usar nada más que throw()
para indicar que no arroja ninguna excepción.
Una especificación "throw ()" permite al compilador realizar algunas optimizaciones al hacer un análisis de flujo de código si sabe que esa función nunca arrojará una excepción (o al menos promete nunca lanzar una excepción). Larry Osterman habla de esto brevemente aquí:
http://blogs.msdn.com/larryosterman/archive/2006/03/22/558390.aspx
gcc emitirá advertencias cuando viole las especificaciones de excepción. Lo que hago es usar macros para usar las especificaciones de excepción solo en modo "pelusa" y compilarlas expresamente para verificar que las excepciones concuerden con mi documentación.