Literalmente, hay libros enteros escritos sobre este tema, por lo que cualquier respuesta será, en el mejor de los casos, un resumen. Estos son algunos de los puntos importantes que creo que valen la pena, según su pregunta. No es una lista exhaustiva.
Las excepciones están destinadas a NO ser atrapadas por todo el lugar.
Siempre que haya un controlador de excepción general en el bucle principal, dependiendo del tipo de aplicación (servidor web, servicio local, utilidad de línea de comando ...), generalmente tiene todos los controladores de excepción que necesita.
En mi código, solo hay unas pocas declaraciones catch, si es que hay alguna, fuera del ciclo principal. Y ese parece ser el enfoque común en C ++ moderno.
Las excepciones y los códigos de retorno no son mutuamente excluyentes.
No debe hacer de este un enfoque de todo o nada. Las excepciones deben usarse para situaciones excepcionales. Cosas como "Archivo de configuración no encontrado", "Disco lleno" o cualquier otra cosa que no se pueda manejar localmente.
Las fallas comunes, como verificar si un nombre de archivo proporcionado por el usuario es válido, no es un caso de uso para excepciones; Utilice un valor de retorno en esos casos en su lugar.
Como puede ver en los ejemplos anteriores, "archivo no encontrado" puede ser una excepción o un código de retorno, dependiendo del caso de uso: "es parte de la instalación" versus "el usuario puede hacer un error tipográfico".
Entonces no hay una regla absoluta. Una pauta aproximada es: si se puede manejar localmente, conviértalo en un valor de retorno; Si no puede manejarlo localmente, inicie una excepción.
La comprobación estática de excepciones no es útil.
Como las excepciones no se deben manejar localmente de todos modos, generalmente no es importante qué excepciones se pueden lanzar. La única información útil es si se puede lanzar alguna excepción.
Java tiene una comprobación estática, pero generalmente se considera un experimento fallido, y la mayoría de los lenguajes ya que, especialmente C #, no tienen ese tipo de comprobación estática. Esta es una buena lectura sobre las razones por las cuales C # no lo tiene.
Por esas razones, C ++ ha dejado de estar throw(exceptionA, exceptionB)
a favor de noexcept(true)
. El valor predeterminado es que una función puede lanzar, por lo que los programadores deben esperar eso a menos que la documentación explícitamente prometa lo contrario.
Escribir código seguro de excepciones no tiene nada que ver con escribir controladores de excepciones.
¡Prefiero decir que escribir código seguro de excepción se trata de cómo evitar escribir manejadores de excepción!
La mayoría de las mejores prácticas tienen la intención de reducir el número de manejadores de excepciones. Escribir código una vez e invocarlo automáticamente, por ejemplo, a través de RAII, da como resultado menos errores que copiar y pegar el mismo código en todo el lugar.