El manejo de excepciones en C ++ se limita a try / throw / catch. A diferencia de Object Pascal, Java, C # y Python, incluso en C ++ 11, la finally
construcción no se ha implementado.
He visto una gran cantidad de literatura de C ++ sobre "código seguro de excepción". Lippman escribe que el código seguro de excepción es un tema importante pero avanzado y difícil, más allá del alcance de su Manual, lo que parece implicar que el código seguro no es fundamental para C ++. ¡Herb Sutter dedica 10 capítulos al tema en su Excepcional C ++!
Sin embargo, me parece que muchos de los problemas encontrados al intentar escribir "código seguro de excepción" podrían resolverse bastante bien si finally
se implementara la construcción, lo que le permite al programador asegurarse de que incluso en caso de una excepción, el programa pueda restaurarse a un estado seguro, estable y sin fugas, cerca del punto de asignación de recursos y código potencialmente problemático. Como programador muy experimentado de Delphi y C #, uso try .. finalmente bloquea bastante en mi código, al igual que la mayoría de los programadores en estos lenguajes.
Teniendo en cuenta todas las 'campanas y silbatos' implementados en C ++ 11, me sorprendió descubrir que 'finalmente' todavía no estaba allí.
Entonces, ¿por qué la finally
construcción nunca se ha implementado en C ++? Realmente no es un concepto muy difícil o avanzado de comprender y ayuda mucho al programador a escribir 'código seguro de excepción'.
fuente
finally
en C ++, y qué técnicas para el manejo de excepciones se utilizan en su lugar?" es válido y está en el tema de este sitio. Las respuestas existentes cubren esto bien, creo. Convirtiéndolo en una discusión sobre "¿Sonfinally
valiosas las razones de los diseñadores de C ++ para no incluirlas ?" y "¿Deberíafinally
agregarse a C ++?" y continuar la discusión a través de los comentarios sobre la pregunta y cada respuesta no se ajusta al modelo de este sitio de preguntas y respuestas.Respuestas:
Como comentario adicional sobre la respuesta de @ Nemanja (que, dado que cita a Stroustrup, es realmente una respuesta tan buena como puedes obtener):
Realmente es solo una cuestión de entender la filosofía y los modismos de C ++. Tome su ejemplo de una operación que abre una conexión de base de datos en una clase persistente y tiene que asegurarse de que cierra esa conexión si se produce una excepción. Esta es una cuestión de seguridad excepcional y se aplica a cualquier lenguaje con excepciones (C ++, C #, Delphi ...).
En un lenguaje que usa
try
/finally
, el código podría verse así:Simple y directo. Sin embargo, hay algunas desventajas:
finally
bloque, de lo contrario pierdo recursos.DoRiskyOperation
trata de más de una llamada a un método, si tengo que procesar algo en eltry
bloque, entonces laClose
operación puede terminar estando un poco lejos de laOpen
operación. No puedo escribir mi limpieza justo al lado de mi adquisición.try
/finally
bloques.El enfoque de C ++ se vería así:
Esto resuelve completamente todas las desventajas del
finally
enfoque. Tiene algunas desventajas propias, pero son relativamente menores:ScopedDatabaseConnection
clase tú mismo. Sin embargo, es una implementación muy simple: solo 4 o 5 líneas de código.Personalmente, considerando estas ventajas y desventajas, considero que RAII es una técnica mucho más preferible
finally
. Su experiencia puede ser diferente.Finalmente, debido a que RAII es un idioma muy bien establecido en C ++, y para aliviar a los desarrolladores de la carga de escribir numerosas
Scoped...
clases, hay bibliotecas como ScopeGuard y Boost.ScopeExit que facilitan este tipo de limpieza determinista.fuente
using
declaración, que limpia automáticamente cualquier objeto que implemente laIDisposable
interfaz. Entonces, aunque es posible equivocarse, es bastante fácil hacerlo bien.try/finally
construcción porque el compilador no expone unatry/finally
construcción y la única forma de acceder a ella es a través de la clase diseño idiomático, no es una "ventaja"; Es la definición misma de una inversión de abstracción.finally
. Como dije, su kilometraje puede variar.¿ Por qué C ++ no proporciona una construcción "finalmente"? en Preguntas frecuentes sobre el estilo y la técnica C ++ de Bjarne Stroustrup :
fuente
finally
construcción siempre sea inútil para siempre, a pesar de lo que Strousup está diciendo. El simple hecho de que escribir "código seguro de excepción" es un gran problema en C ++ es prueba de ello. Heck, C # tiene dos destructores yfinally
, y que tanto se acostumbre.La razón por la que C ++ no tiene
finally
es porque no se necesita en C ++.finally
se usa para ejecutar algún código independientemente de si se produjo una excepción o no, lo que casi siempre es algún tipo de código de limpieza. En C ++, este código de limpieza debe estar en el destructor de la clase relevante y siempre se llamará al destructor, como unfinally
bloque. El idioma de usar el destructor para su limpieza se llama RAII .Dentro de la comunidad C ++ puede que se hable más sobre el código de "excepción segura", pero es casi igualmente importante en otros lenguajes que tienen excepciones. El punto principal del código de 'excepción segura' es que usted piensa en qué estado queda su código si ocurre una excepción en cualquiera de las funciones / métodos que llama.
En C ++, el código de 'excepción segura' es un poco más importante, porque C ++ no tiene recolección automática de basura que se encarga de los objetos que quedan huérfanos debido a una excepción.
La razón por la cual la seguridad de excepción se discute más en la comunidad de C ++ probablemente también se debe al hecho de que en C ++ debe ser más consciente de lo que puede salir mal, porque hay menos redes de seguridad predeterminadas en el lenguaje.
fuente
finally
al estándar C ++, creo que es seguro concluir que la comunidad C ++ no considerathe absence of finally
un problema. La mayoría de los lenguajes quefinally
carecen de la destrucción determinista consistente que tiene C ++. Veo que Delphi los tiene a ambos, pero no conozco su historia lo suficientemente bien como para saber cuál estuvo allí primero.finally
". Nunca recuerdo ninguna tarea que hubiera facilitado si hubiera tenido acceso a ella.Otros han discutido RAII como la solución. Es una solución perfectamente buena. Pero eso realmente no aborda por qué no agregaron
finally
también, ya que es algo muy deseado. La respuesta a eso es más fundamental para el diseño y desarrollo de C ++: durante el desarrollo de C ++, los involucrados se han resistido fuertemente a la introducción de características de diseño que se pueden lograr utilizando otras características sin una gran cantidad de complicaciones y especialmente cuando esto requiere la introducción de nuevas palabras clave que podrían hacer que el código anterior sea incompatible. Dado que RAII proporciona una alternativa altamente funcionalfinally
yfinally
, de todos modos, puede implementar la suya propia en C ++ 11, hubo poca necesidad de hacerlo.Todo lo que necesita hacer es crear una clase
Finally
que llame a la función pasada a su constructor en su destructor. Entonces puedes hacer esto:Sin embargo, la mayoría de los programadores nativos de C ++ preferirán, en general, objetos RAII de diseño limpio.
fuente
Finally atEnd([&] () { database.close(); });
también, me imagino que lo siguiente es mejor:{ Finally atEnd(...); try {...} catch(e) {...} }
(saqué el finalizador del bloque de prueba para que se ejecute después de los bloques de captura).Puede usar un patrón de "trampa", incluso si no desea usar el bloque try / catch.
Poner un objeto simple en el alcance requerido. En el destructor de este objeto pon tu lógica "final". No importa qué, cuando la pila se desenrolla, se llamará al destructor del objeto y obtendrás tus dulces.
fuente
Bueno, podría hacer una especie de roll-own
finally
, usando Lambdas, que obtendría lo siguiente para compilar bien (usando un ejemplo sin RAII, por supuesto, no es el mejor código):Ver este artículo .
fuente
No estoy seguro de estar de acuerdo con las afirmaciones aquí de que RAII es un superconjunto
finally
. El talón de Aquiles de RAII es simple: excepciones. RAII se implementa con destructores, y siempre está mal en C ++ tirar de un destructor. Eso significa que no puede usar RAII cuando necesita su código de limpieza para lanzar. Sifinally
se implementaran, por otro lado, no hay razón para creer que no sería legal lanzar desde unfinally
bloque.Considere una ruta de código como esta:
Si tuviéramos
finally
, podríamos escribir:Pero no hay manera, que pueda encontrar, de obtener el comportamiento equivalente usando RAII.
Si alguien sabe cómo hacer esto en C ++, estoy muy interesado en la respuesta. Incluso estaría contento con algo que se basara, por ejemplo, en hacer cumplir todas las excepciones heredadas de una sola clase con algunas capacidades especiales o algo así.
fuente
complex_cleanup
puede lanzar, entonces podría tener un caso en el que dos excepciones no detectadas están en vuelo al mismo tiempo, tal como lo haría con RAII / destructores, y C ++ se niega a permitir esto. Si desea que se vea la excepción original,complex_cleanup
debe evitar cualquier excepción, tal como lo haría con RAII / destructores. Si deseacomplex_cleanup
que se vea la excepción, entonces creo que puede usar bloques try / catch anidados, aunque esta es una tangente y difícil de encajar en un comentario, por lo que vale la pena hacer una pregunta por separado.finally
bloque supuesto funcionaría claramente igual que un lanzamiento en uncatch
bloque WRT, excepto en vuelo, no en llamadasstd::terminate
. La pregunta es "¿por qué nofinally
en C ++?" y todas las respuestas dicen "no lo necesitas ... RAII FTW!" Mi punto es que sí, RAII está bien para casos simples como la administración de memoria, pero hasta que se resuelva el problema de las excepciones, se requiere demasiado pensamiento / sobrecarga / preocupación / rediseño para ser una solución de propósito general.