En la escuela, hace más de 10 años, te estaban enseñando a usar especificadores de excepción. Dado que mi experiencia es como uno de ellos programadores de Torvaldish C que obstinadamente evita C ++ a menos que sea forzado a hacerlo, solo termino en C ++ esporádicamente, y cuando lo hago, todavía uso especificadores de excepción, ya que eso es lo que me enseñaron.
Sin embargo, la mayoría de los programadores de C ++ parecen desaprobar los especificadores de excepción. He leído el debate y los argumentos de varios gurús de C ++, como estos . Por lo que yo entiendo, se reduce a tres cosas:
- Los especificadores de excepción usan un sistema de tipos que es inconsistente con el resto del lenguaje ("sistema de tipo de sombra").
- Si su función con un especificador de excepción arroja algo más que lo que ha especificado, el programa se terminará de manera incorrecta e inesperada.
- Los especificadores de excepción se eliminarán en el próximo estándar de C ++.
¿Me estoy perdiendo algo aquí o son todas estas razones?
Mis propias opiniones
Con respecto a 1): ¿Y qué? C ++ es probablemente el lenguaje de programación más inconsistente jamás creado, en cuanto a sintaxis. Tenemos las macros, las goto / etiquetas, la horda (¿acaparamiento?) De comportamiento indefinido / no especificado / implementado, los tipos enteros mal definidos, todas las reglas implícitas de promoción de tipos, palabras clave de casos especiales como amigo, auto , registro, explícito ... Y así sucesivamente. Alguien probablemente podría escribir varios libros gruesos de todas las rarezas en C / C ++. Entonces, ¿por qué las personas reaccionan contra esta inconsistencia particular, que es un defecto menor en comparación con muchas otras características mucho más peligrosas del lenguaje?
Con respecto a 2): ¿No es mi responsabilidad? Hay muchas otras formas en que puedo escribir un error fatal en C ++, ¿por qué este caso particular es peor? En lugar de escribir throw(int)
y luego lanzar Crash_t, también puedo afirmar que mi función devuelve un puntero a int, luego hacer un tipo de letra explícito y explícito y devolver un puntero a un Crash_t. El espíritu de C / C ++ siempre ha sido dejar la mayor parte de la responsabilidad al programador.
¿Qué pasa con las ventajas entonces? Lo más obvio es que si su función intenta arrojar explícitamente cualquier tipo que no sea el que especificó, el compilador le dará un error. Creo que el estándar es claro con respecto a esto (?). Los errores solo sucederán cuando su función llame a otras funciones que a su vez arrojan el tipo incorrecto.
Al provenir de un mundo de programas deterministas e integrados en C, sin duda preferiría saber exactamente qué me arrojará una función. Si hay algo en el lenguaje que lo respalde, ¿por qué no usarlo? Las alternativas parecen ser:
void func() throw(Egg_t);
y
void func(); // This function throws an Egg_t
Creo que hay una gran posibilidad de que la persona que llama ignore / olvide implementar el try-catch en el segundo caso, y menos en el primer caso.
Según tengo entendido, si cualquiera de estas dos formas decide lanzar repentinamente otro tipo de excepción, el programa se bloqueará. En el primer caso porque no se le permite lanzar otra excepción, en el segundo caso porque nadie esperaba que arrojara un SpanishInquisition_t y, por lo tanto, esa expresión no se capta donde debería haber estado.
En el caso de esto último, tener alguna captura de último recurso (...) en el nivel más alto del programa realmente no parece mejor que un bloqueo del programa: "Oye, en algún lugar de tu programa algo arrojó una excepción extraña y no controlada ". No puede recuperar el programa una vez que esté tan lejos de donde se produjo la excepción, lo único que puede hacer es salir del programa.
Y desde el punto de vista del usuario, no podría importarles menos si recibe un cuadro de mensaje malicioso del sistema operativo que dice "Programa terminado. Blablabla en la dirección 0x12345" o un cuadro de mensaje maligno de su programa que dice "Excepción no controlada: myclass. func.something ". El error sigue ahí.
Con el próximo estándar C ++ no tendré otra opción que abandonar los especificadores de excepción. Pero preferiría escuchar algún argumento sólido de por qué son malos, en lugar de "Su Santidad lo ha declarado y así es así". ¿Quizás hay más argumentos en contra de ellos que los que enumeré, o tal vez hay más de ellos de lo que me doy cuenta?
Respuestas:
Las especificaciones de excepción son malas porque se aplican de manera débil y, por lo tanto, en realidad no logran mucho, y también son malas porque obligan al tiempo de ejecución a verificar excepciones inesperadas para que puedan terminar (), en lugar de invocar UB , esto puede desperdiciar una cantidad significativa de rendimiento.
En resumen, las especificaciones de excepción no se aplican con la suficiente fuerza en el lenguaje como para hacer que el código sea más seguro, e implementarlas según lo especificado fue una gran pérdida de rendimiento.
fuente
Una razón por la que nadie los usa es porque su suposición principal es incorrecta:
Este programa se compila sin errores ni advertencias .
Además, aunque se hubiera detectado la excepción , el programa aún finaliza.
Editar: para responder un punto en su pregunta, catch (...) (o mejor, catch (std :: exeption &) u otra clase base, y luego catch (...)) sigue siendo útil incluso si no lo hace saber exactamente lo que salió mal.
Tenga en cuenta que su usuario ha pulsado un botón de menú para "guardar". El controlador del botón de menú invita a la aplicación a guardar. Esto podría fallar por innumerables razones: el archivo estaba en un recurso de red que desapareció, había un archivo de solo lectura y no se pudo guardar, etc. Pero al controlador realmente no le importa por qué algo falló; solo le importa que haya tenido éxito o haya fallado. Si tuvo éxito, todo está bien. Si falla, puede decirle al usuario. La naturaleza exacta de la excepción es irrelevante. Además, si escribe un código adecuado y seguro para excepciones, significa que cualquier error de este tipo puede propagarse a través de su sistema sin desactivarlo, incluso para el código que no se preocupa por el error. Esto hace que sea más fácil extender el sistema. Por ejemplo, ahora guarda a través de una conexión de base de datos.
fuente
Esta entrevista con Anders Hejlsberg es bastante famosa. En él, explica por qué el equipo de diseño de C # descartó las excepciones verificadas en primer lugar. En pocas palabras, hay dos razones principales: versionabilidad y escalabilidad.
Sé que el OP se está centrando en C ++, mientras que Hejlsberg está discutiendo C #, pero los puntos que Hejlsberg hace son perfectamente aplicables a C ++ también.
fuente