¿Usted (realmente) escribe código seguro de excepción? [cerrado]

312

El manejo de excepciones (EH) parece ser el estándar actual, y al buscar en la web, no puedo encontrar ideas o métodos novedosos que intenten mejorarlo o reemplazarlo (bueno, existen algunas variaciones, pero nada novedoso).

Aunque la mayoría de la gente parece ignorarlo o simplemente aceptarlo, EH tiene algunos inconvenientes enormes: las excepciones son invisibles para el código y crea muchos, muchos puntos de salida posibles. Joel sobre software escribió un artículo al respecto . La comparación con los gotoajustes perfectos me hizo pensar nuevamente en EH.

Intento evitar EH y solo uso valores de retorno, devoluciones de llamada o lo que sea apropiado para el propósito. Pero cuando tiene que escribir código confiable, simplemente no puede ignorar EH en estos días : comienza con el new, que puede generar una excepción, en lugar de simplemente devolver 0 (como en los viejos tiempos). Esto hace que cualquier línea de código C ++ sea vulnerable a una excepción. Y luego más lugares en el código fundacional de C ++ lanzan excepciones ... std lib lo hace, y así sucesivamente.

Esto se siente como caminar sobre terrenos inestables . Entonces, ¡ahora nos vemos obligados a tener cuidado con las excepciones!

Pero es difícil, es realmente difícil. Debe aprender a escribir código seguro de excepción, e incluso si tiene algo de experiencia con él, ¡aún será necesario que verifique nuevamente cualquier línea de código para estar seguro! O comienzas a poner bloques try / catch en todas partes, lo que satura el código hasta que alcanza un estado de ilegibilidad.

EH reemplazó el antiguo enfoque determinista limpio (valores de retorno ...), que tenía solo algunos inconvenientes, pero comprensibles y fácilmente solucionables, con un enfoque que crea muchos puntos de salida posibles en su código, y si comienza a escribir código que atrapa excepciones (qué se ven obligados a hacerlo en algún momento), incluso crea una multitud de rutas a través de su código (código en los bloques catch, piense en un programa de servidor donde necesita instalaciones de registro que no sean std :: cerr ..). EH tiene ventajas, pero ese no es el punto.

Mis preguntas actuales:

  • ¿Realmente escribes código seguro de excepción?
  • ¿Está seguro de que su último código "listo para producción" es seguro para excepciones?
  • ¿Puedes estar seguro de que lo es?
  • ¿Conoces y / o utilizas alternativas que funcionan?
Frunsi
fuente
44
"EH reemplazó el viejo enfoque determinista limpio (valores de retorno ...)" ¿Qué? Las excepciones son tan deterministas como los códigos de retorno. No hay nada al azar sobre ellos.
rlbond 05 de
2
EH ha sido el estándar durante mucho tiempo (desde Ada incluso!)
12
¿Es "EH" una abreviatura establecida para el manejo de excepciones? Nunca lo he visto antes.
Thomas Padron-McCarthy
3
Las leyes de Newton son aplicables hoy para la mayoría de las cosas. El hecho de que hayan predicho una gama muy amplia de fenómenos durante siglos es significativo.
David Thornley
44
@frunsi: Je, perdón por confundirte! Pero creo que, en todo caso, un acrónimo o abreviatura extraño e inexplicable desanimará a las personas más que no.
Thomas Padron-McCarthy

Respuestas:

520

Su pregunta hace una afirmación, que "Escribir código seguro de excepción es muy difícil". Primero responderé sus preguntas y luego responderé la pregunta oculta detrás de ellas.

Respondiendo preguntas

¿Realmente escribes código seguro de excepción?

Por supuesto que sí.

Esta es la razón por la que Java perdió mucho de su atractivo para mí como programador de C ++ (falta de semántica RAII), pero estoy divagando: esta es una pregunta de C ++.

De hecho, es necesario cuando necesita trabajar con código STL o Boost. Por ejemplo, los subprocesos de C ++ ( boost::threado std::thread) generarán una excepción para salir con gracia.

¿Está seguro de que su último código "listo para producción" es seguro para excepciones?

¿Puedes estar seguro de que lo es?

Escribir código seguro de excepción es como escribir código libre de errores.

No puede estar 100% seguro de que su código sea seguro para excepciones. Pero luego, se esfuerza por lograrlo, utilizando patrones conocidos y evitando los antipatrones conocidos.

¿Conoces y / o utilizas alternativas que funcionan?

No hay alternativas viables en C ++ (es decir, deberá volver a C y evitar las bibliotecas de C ++, así como sorpresas externas como Windows SEH).

Escribir código seguro de excepción

Para escribir un código de seguridad de excepción, primero debe saber qué nivel de seguridad de excepción tiene cada instrucción que escriba.

Por ejemplo, a newpuede lanzar una excepción, pero la asignación de un incorporado (por ejemplo, un int o un puntero) no fallará. Un intercambio nunca fallará (nunca escriba un intercambio de lanzamiento), un std::list::push_backlanzamiento de latas ...

Garantía de excepción

Lo primero que debe entender es que debe poder evaluar la garantía de excepción que ofrecen todas sus funciones:

  1. ninguno : su código nunca debe ofrecer eso. Este código filtrará todo y se descompondrá en la primera excepción lanzada.
  2. básico : esta es la garantía que debe ofrecer al menos, es decir, si se lanza una excepción, no se filtran recursos y todos los objetos aún están completos
  3. fuerte : el procesamiento tendrá éxito o arrojará una excepción, pero si arroja, entonces los datos estarán en el mismo estado que si el procesamiento no hubiera comenzado en absoluto (esto le da un poder transaccional a C ++)
  4. nothrow / nofail : el procesamiento tendrá éxito.

Ejemplo de código

El siguiente código parece correcto C ++, pero en verdad, ofrece la garantía "ninguno" y, por lo tanto, no es correcto:

void doSomething(T & t)
{
   if(std::numeric_limits<int>::max() > t.integer)  // 1.   nothrow/nofail
      t.integer += 1 ;                              // 1'.  nothrow/nofail
   X * x = new X() ;                // 2. basic : can throw with new and X constructor
   t.list.push_back(x) ;            // 3. strong : can throw
   x->doSomethingThatCanThrow() ;   // 4. basic : can throw
}

Escribo todo mi código con este tipo de análisis en mente.

La garantía más baja que se ofrece es básica, pero luego, el orden de cada instrucción hace que toda la función sea "ninguna", porque si 3. arroja, x se perderá.

Lo primero que debe hacer es hacer que la función sea "básica", es decir, poner x en un puntero inteligente hasta que sea propiedad de la lista de forma segura:

void doSomething(T & t)
{
   if(std::numeric_limits<int>::max() > t.integer)  // 1.   nothrow/nofail
      t.integer += 1 ;                              // 1'.  nothrow/nofail
   std::auto_ptr<X> x(new X()) ;    // 2.  basic : can throw with new and X constructor
   X * px = x.get() ;               // 2'. nothrow/nofail
   t.list.push_back(px) ;           // 3.  strong : can throw
   x.release() ;                    // 3'. nothrow/nofail
   px->doSomethingThatCanThrow() ;  // 4.  basic : can throw
}

Ahora, nuestro código ofrece una garantía "básica". Nada se escapará y todos los objetos estarán en un estado correcto. Pero podríamos ofrecer más, es decir, la garantía sólida. Aquí es donde puede llegar a ser costoso, y es por eso que no todo el código C ++ es fuerte. Vamos a intentarlo:

void doSomething(T & t)
{
   // we create "x"
   std::auto_ptr<X> x(new X()) ;    // 1. basic : can throw with new and X constructor
   X * px = x.get() ;               // 2. nothrow/nofail
   px->doSomethingThatCanThrow() ;  // 3. basic : can throw

   // we copy the original container to avoid changing it
   T t2(t) ;                        // 4. strong : can throw with T copy-constructor

   // we put "x" in the copied container
   t2.list.push_back(px) ;          // 5. strong : can throw
   x.release() ;                    // 6. nothrow/nofail
   if(std::numeric_limits<int>::max() > t2.integer)  // 7.   nothrow/nofail
      t2.integer += 1 ;                              // 7'.  nothrow/nofail

   // we swap both containers
   t.swap(t2) ;                     // 8. nothrow/nofail
}

Reordenamos las operaciones, primero creando y configurando Xsu valor correcto. Si alguna operación falla, entonces tno se modifica, por lo tanto, la operación 1 a 3 puede considerarse "fuerte": si algo arroja, tno se modifica y Xno se filtrará porque es propiedad del puntero inteligente.

Luego, creamos una copia t2de t, y trabajamos en esta copia de la operación 4 a 7. Si algo arroja, t2se modifica, pero entonces, tsigue siendo el original. Todavía ofrecemos la garantía fuerte.

Luego, intercambiamos ty t2. Las operaciones de intercambio no deben arrojarse en C ++, por lo tanto, esperemos que el intercambio para el que escribió no Tsea ​​un lanzamiento (si no es así, vuelva a escribirlo para que no sea un lanzamiento).

Entonces, si llegamos al final de la función, todo tiene éxito (No es necesario un tipo de retorno) y ttiene su valor exceptuado. Si falla, taún tiene su valor original.

Ahora, ofrecer la garantía sólida podría ser bastante costoso, así que no se esfuerce por ofrecer la garantía sólida a todo su código, pero si puede hacerlo sin costo (y la incorporación de C ++ y otras optimizaciones podrían hacer que todo el código anterior sea gratuito) , entonces hacerlo. El usuario de la función te lo agradecerá.

Conclusión

Se necesita algún hábito para escribir código seguro de excepción. Deberá evaluar la garantía ofrecida por cada instrucción que utilizará y luego deberá evaluar la garantía ofrecida por una lista de instrucciones.

Por supuesto, el compilador de C ++ no hará una copia de seguridad de la garantía (en mi código, ofrezco la garantía como una etiqueta @warning doxygen), lo cual es un poco triste, pero no debería impedir que intente escribir código seguro de excepción.

Falla normal vs. error

¿Cómo puede un programador garantizar que una función sin fallas siempre tenga éxito? Después de todo, la función podría tener un error.

Esto es verdad. Se supone que las garantías de excepción se ofrecen mediante un código libre de errores. Pero luego, en cualquier idioma, llamar a una función supone que la función está libre de errores. Ningún código cuerdo se protege contra la posibilidad de que tenga un error. Escriba el código lo mejor que pueda, y luego, ofrezca la garantía con la suposición de que está libre de errores. Y si hay un error, corríjalo.

Las excepciones son para fallas de procesamiento excepcionales, no para errores de código.

Ultimas palabras

Ahora, la pregunta es "¿Vale la pena?".

Por supuesto que es. Tener una función "nothrow / no-fail" sabiendo que la función no fallará es una gran bendición. Lo mismo puede decirse de una función "fuerte", que le permite escribir código con semántica transaccional, como bases de datos, con funciones de confirmación / reversión, la confirmación es la ejecución normal del código, arrojando excepciones siendo la reversión.

Entonces, lo "básico" es la mínima garantía que debe ofrecer. C ++ es un lenguaje muy fuerte allí, con sus ámbitos, lo que le permite evitar cualquier pérdida de recursos (algo que un recolector de basura encontraría difícil de ofrecer para la base de datos, la conexión o los identificadores de archivo).

Así, por lo que yo veo, es digno de él.

Editar 2010-01-29: Acerca del intercambio sin lanzamiento

nobar hizo un comentario que creo que es bastante relevante, porque es parte de "cómo se escribe el código seguro de excepción":

  • [yo] Un intercambio nunca fallará (ni siquiera escriba un intercambio de lanzamiento)
  • [nobar] Esta es una buena recomendación para swap()funciones escritas a medida . Cabe señalar, sin embargo, que std::swap()puede fallar en función de las operaciones que utiliza internamente

el valor predeterminado std::swaphará copias y asignaciones que, para algunos objetos, pueden arrojar. Por lo tanto, el intercambio predeterminado podría arrojarse, ya sea utilizado para sus clases o incluso para clases STL. En lo que respecta al estándar C ++, la operación de intercambio para vector, dequey listno arrojará, mientras que podría hacerlo mapsi el functor de comparación puede lanzar en la construcción de la copia (Ver El lenguaje de programación C ++, Edición especial, apéndice E, E.4.3 .Swap ).

Al observar la implementación de Visual C ++ 2008 del intercambio del vector, el intercambio del vector no se lanzará si los dos vectores tienen el mismo asignador (es decir, el caso normal), pero hará copias si tienen asignadores diferentes. Y así, supongo que podría arrojar en este último caso.

Por lo tanto, el texto original aún se mantiene: nunca escriba un intercambio de lanzamiento, pero debe recordarse el comentario de nobar: asegúrese de que los objetos que está intercambiando tengan un intercambio de no lanzamiento.

Editar 2011-11-06: artículo interesante

Dave Abrahams , quien nos brindó las garantías básicas / fuertes / no críticas , describió en un artículo su experiencia sobre cómo hacer que la excepción STL sea segura:

http://www.boost.org/community/exception_safety.html

Mire el séptimo punto (Pruebas automatizadas para seguridad de excepción), donde confía en las pruebas unitarias automatizadas para asegurarse de que se analicen todos los casos. Supongo que esta parte es una excelente respuesta a la pregunta del autor " ¿Puedes estar seguro de que lo es? ".

Editar 2013-05-31: Comentario de dionadar

t.integer += 1;es sin la garantía de que el desbordamiento no sucederá NO es una excepción segura, y de hecho técnicamente puede invocar a UB. (El desbordamiento firmado es UB: C ++ 11 5/4 "Si durante la evaluación de una expresión, el resultado no está matemáticamente definido o no está en el rango de valores representables para su tipo, el comportamiento no está definido".) Tenga en cuenta que sin signo los enteros no se desbordan, pero hacen sus cálculos en una clase de equivalencia módulo 2 ^ # bits.

Dionadar se refiere a la siguiente línea, que de hecho tiene un comportamiento indefinido.

   t.integer += 1 ;                 // 1. nothrow/nofail

La solución aquí es verificar si el número entero ya está en su valor máximo (usando std::numeric_limits<T>::max()) antes de hacer la suma.

Mi error iría en la sección "Error normal vs. error", es decir, un error. No invalida el razonamiento, y no significa que el código seguro de excepción sea inútil porque es imposible de lograr. No puede protegerse contra el apagado de la computadora, o los errores del compilador, o incluso sus errores u otros errores. No puedes alcanzar la perfección, pero puedes intentar acercarte lo más posible.

Corregí el código con el comentario de Dionadar en mente.

paercebal
fuente
66
¡Muchas gracias! Todavía esperaba algo diferente. Pero acepto su respuesta por apegarse a C ++ y por su explicación detallada. Incluso refutó mi ofensa "Esto hace que cualquier línea de código C ++ sea vulnerable a una excepción". Este análisis línea por línea tiene sentido después de todo ... Tengo que pensarlo.
Frunsi
8
"Un intercambio nunca fallará". Esta es una buena recomendación para las funciones swap () escritas a medida. Sin embargo, debe tenerse en cuenta que std :: swap () puede fallar en función de las operaciones que utiliza internamente.
nobar
77
@Mehrdad:: "finally" does exactly what you were trying to achievePor supuesto que sí. Y debido a que es fácil producir código frágil finally, la noción "probar con recursos" finalmente se introdujo en Java 7 (es decir, 10 años después de C # usingy 3 décadas después de los destructores de C ++). Esta es la fragilidad que critico. En cuanto a Just because [finally] doesn't match your taste (RAII doesn't match mine, [...]) doesn't mean it's "failing": en esto, la industria no está de acuerdo con su gusto, ya que los lenguajes recolectados de basura tienden a agregar declaraciones inspiradas en RAII (C # usingy Java try).
paercebal
55
@Mehrdad:: RAII doesn't match mine, since it needs a new struct every single darn time, which is tedious sometimesNo, no lo hace. Puede utilizar punteros inteligentes o clases de utilidad para "proteger" los recursos.
paercebal
55
@Mehrdad: "te obliga a hacer un contenedor para algo para lo que quizás no necesites un contenedor" - cuando codifiques en modo RAII, no necesitarás ningún tipo de contenedor: el recurso es el objeto y la vida útil de los objetos es la vida útil de los recursos. Sin embargo, yo vengo de un mundo C ++, actualmente lucho con un proyecto Java. En comparación con RAII en C ++, ¡esa "gestión de recursos manual" en Java ES UNA MENSAJE! Mi opinión, pero Java hace mucho tiempo cambió "gestión automática de memoria" por "gestión automática de recursos".
Frunsi
32

Escribir código seguro para excepciones en C ++ no se trata tanto de usar muchos bloques try {} catch {}. Se trata de documentar qué tipo de garantías proporciona su código.

Recomiendo leer la serie Guru Of The Week de Herb Sutter , en particular las entregas 59, 60 y 61.

Para resumir, hay tres niveles de seguridad de excepción que puede proporcionar:

  • Básico: cuando su código arroja una excepción, su código no pierde recursos y los objetos permanecen destructibles.
  • Fuerte: cuando su código arroja una excepción, deja el estado de la aplicación sin cambios.
  • Sin lanzamiento: su código nunca arroja excepciones.

Personalmente, descubrí estos artículos bastante tarde, por lo que gran parte de mi código C ++ definitivamente no es seguro para excepciones.

Joh
fuente
1
Su libro "Excepcional C ++" también es una buena lectura. Pero todavía trato de cuestionar el concepto de EH ...
Frunsi
8
+1 OP combina excepciones de manejo (capturarlas) con seguridad de excepción (generalmente más sobre RAII)
jk.
Sospecho que muy poco código C ++ de producción es seguro para excepciones.
Raedwald
18

Algunos de nosotros hemos estado usando la excepción por más de 20 años. PL / I los tiene, por ejemplo. La premisa de que son una tecnología nueva y peligrosa me parece cuestionable.

bmargulies
fuente
Por favor, no me malinterpreten, estoy (o estoy tratando de) cuestionar EH. Y especialmente C ++ EH. Y estoy buscando alternativas. Tal vez tenga que aceptarlo (y lo haré si es la única forma), pero creo que podría haber mejores alternativas. No es que yo creo que el concepto es nuevo, pero sí, creo que puede ser más peligroso que el tratamiento de errores explícita con los códigos de retorno ...
Frunsi
1
Si no te gusta, no lo uses. Ponga bloques de prueba alrededor de las cosas que necesita llamar que puedan arrojar, y reinvente el código de error antiguo y sufra con los problemas que tiene, que en algunos casos son tolerables.
bmargulies
Ok, perfecto, solo usaré EH y códigos de error, y viviré con eso. Soy un imbécil, ¡debería haber llegado a esa solución por mi cuenta! ;)
Frunsi
17

En primer lugar (como dijo Neil), SEH es el Manejo de excepciones estructuradas de Microsoft. Es similar pero no idéntico al procesamiento de excepciones en C ++. De hecho, debe habilitar el Control de excepciones de C ++ si lo desea en Visual Studio: ¡el comportamiento predeterminado no garantiza que los objetos locales se destruyan en todos los casos! En cualquier caso, el manejo de excepciones no es realmente más difícil , solo es diferente .

Ahora para sus preguntas reales.

¿Realmente escribes código seguro de excepción?

Si. Me esfuerzo por un código de excepción seguro en todos los casos. Evangelizo usando técnicas RAII para el acceso al alcance de los recursos (p. Ej., boost::shared_ptrPara memoria, boost::lock_guardpara bloqueo). En general, el uso constante de RAII y las técnicas de protección de alcance harán que el código seguro de excepción sea mucho más fácil de escribir. El truco es aprender qué existe y cómo aplicarlo.

¿Está seguro de que su último código "listo para producción" es seguro para excepciones?

No. Es tan seguro como es. Puedo decir que no he visto una falla en el proceso debido a una excepción en varios años de actividad 24/7. No espero un código perfecto, solo un código bien escrito. Además de proporcionar una seguridad excepcional, las técnicas anteriores garantizan la corrección de una manera que es casi imposible de lograr con try/ catchblocks. Si está atrapando todo en su ámbito de control superior (hilo, proceso, etc.), puede estar seguro de que continuará ejecutándose ante las excepciones (la mayoría de las veces ). Las mismas técnicas también lo ayudarán a continuar ejecutándose correctamente ante excepciones sin try/ catchbloques en todas partes .

¿Puedes estar seguro de que es así?

Si. Puede estar seguro mediante una exhaustiva auditoría de código, pero ¿nadie realmente hace eso? Sin embargo, las revisiones periódicas de código y los desarrolladores cuidadosos hacen mucho para llegar allí.

¿Conoces y / o utilizas alternativas que funcionan?

He intentado algunas variaciones a lo largo de los años, como los estados de codificación en los bits superiores (ala HRESULTs ) o ese horrible setjmp() ... longjmp()truco. Ambos se descomponen en la práctica, aunque de maneras completamente diferentes.


Al final, si te acostumbras a aplicar algunas técnicas y piensas cuidadosamente dónde puedes hacer algo en respuesta a una excepción, terminarás con un código muy legible que es seguro para las excepciones. Puede resumir esto siguiendo estas reglas:

  • Solo desea ver try/ catchcuándo puede hacer algo sobre una excepción específica
  • Casi nunca quieres ver un código en bruto newo deleteen código
  • Evitar std::sprintf, snprintfy matrices en general: se usa std::ostringstreampara formatear y reemplazar matrices con std::vectorystd::string
  • En caso de duda, busque la funcionalidad en Boost o STL antes de lanzar su propio

Solo puedo recomendarle que aprenda a usar las excepciones correctamente y que se olvide de los códigos de resultados si planea escribir en C ++. Si desea evitar excepciones, puede considerar escribir en otro idioma que no las tenga o las haga seguras . Si realmente quiere aprender a utilizar C ++ por completo, lea algunos libros de Herb Sutter , Nicolai Josuttis y Scott Meyers .

D.Shawley
fuente
"el comportamiento predeterminado no garantiza que los objetos locales se destruyan en todos los casos" Está diciendo que la configuración predeterminada del compilador de Visual Studio C ++ produce código que es incorrecto ante las excepciones. ¿Es eso realmente así?
Raedwald
"Casi nunca quieres ver un código en bruto newo deleteen código": por raw supongo que te refieres fuera de un constructor o destructor.
Raedwald
@Raedwald - re: VC ++: la edición VS2005 de VC ++ no destruirá objetos locales cuando se produzca una excepción SEH. Lea "habilitar el manejo de excepciones de C ++" . En VS2005, las excepciones SEH no llaman a los destructores de objetos C ++ de forma predeterminada. Si llama a las funciones de Win32 o cualquier cosa definida en una DLL de interfaz C, debe preocuparse por esto, ya que pueden (y ocasionalmente) lanzar una excepción SEH en su camino.
D.Shawley
@Raedwald: re: raw : básicamente, deletenunca debe usarse fuera de la implementación de tr1::shared_ptry similares. newse puede usar siempre que su uso sea similar tr1::shared_ptr<X> ptr(new X(arg, arg));. La parte importante es que el resultado de newva directamente a un puntero administrado. La página de boost::shared_ptrMejores prácticas describe lo mejor.
D.Shawley
¿Por qué se refiere a std :: sprintf (et al) en su conjunto de reglas para seguridad de excepción? Estos no documentan que arrojan excepciones, por ejemplo, en.cppreference.com/w/cpp/io/c/fprintf
mabraham
10

No es posible escribir código seguro de excepción bajo el supuesto de que "cualquier línea puede lanzar". El diseño del código seguro para excepciones se basa de manera crítica en ciertos contratos / garantías que se supone que debe esperar, observar, seguir e implementar en su código. Es absolutamente necesario tener un código que garantice que nunca se lanzará. Existen otros tipos de garantías de excepción por ahí.

En otras palabras, la creación de código seguro para excepciones es en gran medida una cuestión de diseño del programa , no solo una cuestión de codificación simple .

Hormiga
fuente
8
  • ¿Realmente escribes código seguro de excepción?

Bueno, ciertamente tengo la intención de hacerlo.

  • ¿Está seguro de que su último código "listo para producción" es seguro para excepciones?

Estoy seguro de que mis servidores 24/7 creados con excepciones se ejecutan 24/7 y no pierden memoria.

  • ¿Puedes estar seguro de que lo es?

Es muy difícil asegurarse de que cualquier código sea correcto. Por lo general, uno solo puede ir por resultados

  • ¿Conoces y / o utilizas alternativas que funcionan?

No. Usar excepciones es más limpio y fácil que cualquiera de las alternativas que he usado durante los últimos 30 años en la programación.

luego
fuente
30
Esta respuesta no tiene valor.
Matt Joiner el
66
@MattJoiner Entonces la pregunta no tiene valor.
Miles Rout
5

Dejando de lado la confusión entre las excepciones SEH y C ++, debe tener en cuenta que las excepciones se pueden lanzar en cualquier momento y escribir su código con eso en mente. La necesidad de seguridad de excepción es en gran medida lo que impulsa el uso de RAII, punteros inteligentes y otras técnicas modernas de C ++.

Si sigue los patrones bien establecidos, escribir código seguro de excepción no es particularmente difícil, y de hecho es más fácil que escribir código que maneje los retornos de error correctamente en todos los casos.

Mark Bessey
fuente
3

EH es bueno, en general. Pero la implementación de C ++ no es muy amigable, ya que es muy difícil saber qué tan buena es su cobertura de captura de excepciones. Java, por ejemplo, lo hace fácil, el compilador tenderá a fallar si no maneja posibles excepciones.

Señor chico
fuente
2
Es mucho mejor con un uso juicioso de noexcept.
einpoklum
2

Aunque realmente me gusta trabajar con Eclipse y Java (nuevo en Java), porque arroja errores en el editor si le falta un controlador EH. Eso hace que las cosas sean MUCHO más difíciles de olvidar para manejar una excepción ...

Además, con las herramientas IDE, agrega el bloque try / catch u otro bloque catch automáticamente.

Crowe T. Robot
fuente
55
Esa es la diferencia entre las excepciones marcadas (Java) y no marcadas (c ++) (Java también tiene algunas de ellas). La ventaja de las excepciones marcadas es lo que escribió, pero allí tiene sus propias desventajas. Google para los enfoques de diferencia y los diferentes problemas que tienen.
David Rodríguez - dribeas 05 de
2

Algunos de nosotros preferimos lenguajes como Java que nos obligan a declarar todas las excepciones lanzadas por los métodos, en lugar de hacerlos invisibles como en C ++ y C #.

Cuando se hace correctamente, las excepciones son superiores a los códigos de retorno de error, si no es por otra razón que no tiene que propagar fallas manualmente en la cadena de llamadas.

Dicho esto, la programación de la biblioteca API de bajo nivel probablemente debería evitar el manejo de excepciones y atenerse a los códigos de retorno de error.

Según mi experiencia, es difícil escribir un código de manejo de excepciones limpio en C ++. Termino usando new(nothrow)mucho.

David R Tribble
fuente
44
¿Y también estás evitando la mayor parte de la biblioteca estándar? Usar new(std::nothrow)no es suficiente. Por cierto, es más fácil escribir código seguro de excepciones en C ++ que en Java: en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization
avakar
3
La usabilidad de excepciones comprobadas de Java está muy exagerada. De hecho, los lenguajes no Java, NO se consideran un éxito. Esta es la razón por la cual la declaración "throw" en C ++ ahora se considera obsoleta, y C # nunca consideró seriamente implementarlas (esta fue una elección de diseño). Para Java, el siguiente documento podría ser ilustrativo: googletesting.blogspot.com/2009/09/…
paercebal
2
Según mi experiencia, escribir código seguro para excepciones en C ++ no es tan difícil y tiende a generar un código más limpio en general. Por supuesto, tienes que aprender a hacerlo.
David Thornley
2

Hago mi mejor esfuerzo para escribir código seguro de excepción, sí.

Eso significa que me cuido de vigilar qué líneas pueden lanzar. No todos pueden hacerlo, y es muy importante tenerlo en cuenta. La clave es realmente pensar y diseñar su código para satisfacer las garantías de excepción definidas en el estándar.

¿Se puede escribir esta operación para proporcionar la garantía de excepción fuerte? ¿Tengo que conformarme con el básico? ¿Qué líneas pueden arrojar excepciones y cómo puedo asegurarme de que si lo hacen, no corrompan el objeto?

jalf
fuente
2
  • ¿Realmente escribes código seguro de excepción? [No hay tal cosa. Las excepciones son un escudo de papel a los errores a menos que tenga un entorno administrado. Esto se aplica a las primeras tres preguntas.]

  • ¿Conoces y / o utilizas alternativas que funcionan? [¿Alternativa a qué? El problema aquí es que las personas no separan los errores reales del funcionamiento normal del programa. Si es la operación normal del programa (es decir, no se encuentra un archivo), en realidad no se trata de manejo de errores. Si se trata de un error real, no hay forma de "manejarlo" o no es un error real. Su objetivo aquí es averiguar qué salió mal y detener la hoja de cálculo y registrar un error, reiniciar el controlador de su tostadora, o simplemente rezar para que el jetfighter pueda continuar volando incluso cuando su software tiene errores y esperar lo mejor.]

Queso Charles Eli
fuente
0

Mucha gente (incluso diría que la mayoría) lo hace.

Lo que es realmente importante acerca de las excepciones es que si no escribe ningún código de manejo, el resultado es perfectamente seguro y se comporta bien. Demasiado ansioso por entrar en pánico, pero seguro.

Usted necesita activamente cometer errores en los controladores para conseguir algo insegura, y sólo captura (...) {} comparará a ignorar el código de error.

ima
fuente
44
No es verdad. Es muy fácil escribir código que no sea seguro para excepciones. Por ejemplo: f = new foo(); f->doSomething(); delete f;si el método doSomething produce una excepción, entonces tiene una pérdida de memoria.
Kristopher Johnson, el
1
No importaría cuando finalice su programa, ¿verdad? Para continuar la ejecución, tendrá que tragar activamente excepciones. Bueno, hay algunos casos específicos en los que la terminación sin limpieza aún es inaceptable, pero tales situaciones requieren un cuidado especial en cualquier lenguaje y estilo de programación.
ima
Simplemente no puede ignorar las excepciones (y no escribir código de manejo), ni en C ++ ni en el código administrado. Será inseguro y no se comportará bien. Excepto tal vez un código de juguete.
Frunsi
1
Si ignora las excepciones en el código de la aplicación, es posible que el programa NO se comporte bien cuando intervengan recursos externos. Es cierto que el sistema operativo se preocupa por cerrar identificadores de archivos, bloqueos, sockets, etc. Pero no todo se maneja, por ejemplo, puede dejar archivos innecesarios o dañar archivos al escribir en ellos, etc. Si ignora las excepciones, tiene un problema en Java, en C ++ puede trabajar con RAII (pero cuando usa la técnica RAII, probablemente las use porque le importan las excepciones) ...
Frunsi
3
Por favor, no tuerzas mis palabras. Escribí "si no escribes ningún código de manejo", y quise decir exactamente eso. Para ignorar las excepciones, debe escribir el código.
ima