ProgramaciónProgramador de sistemas C++

Hable sobre los mecanismos de trabajo de las excepciones en C++. ¿Cómo manejar los errores correctamente y para qué sirve noexcept?

Supere entrevistas con el asistente de IA Hintsage

Respuesta.

Historia de la pregunta:

El mecanismo de manejo de excepciones se introdujo en C++ para proporcionar un manejo de errores más robusto y estructurado, en contraste con los códigos de retorno. Con los años, la sintaxis se ha ampliado y han surgido especificadores como noexcept para controlar la cancelación de lanzamiento de excepciones desde funciones.

Problema:

Un manejo incorrecto de excepciones a menudo conduce a fugas de memoria, comportamientos indefinidos o caídas de aplicaciones. Si no se considera el lanzamiento de excepciones desde constructores, destructores o al trabajar con recursos, surge un problema serio con el estado del programa.

Solución:

La sintaxis básica es el uso de try-catch. Las excepciones pueden ser de cualquier tipo, pero se recomienda heredar las personalizadas de std::exception. La palabra clave noexcept (C++11+) indica que la función no debería lanzar excepciones; lanzar excepciones desde una función noexcept invoca std::terminate().

Ejemplo de código:

#include <iostream> #include <stdexcept> void func() noexcept(false) { throw std::runtime_error("Error!"); } int main() { try { func(); } catch(const std::exception& ex) { std::cout << ex.what(); } }

Características clave:

  • Las excepciones proporcionan un deshacer automático (unwind) de la pila.
  • noexcept permite al compilador optimizar si la función no lanza.
  • Solo std::exception y sus derivados garantizan la existencia del método what().

Preguntas con trampa.

¿Es aceptable lanzar excepciones en destructores?

No, lanzar una excepción desde un destructor durante el desensamblaje de la pila lleva a std::terminate(). Las excepciones en el destructor deben ser atrapadas dentro del propio destructor.

¿Qué sucede si se lanza una excepción desde una función noexcept?

Se invocará std::terminate, el programa finalizará de manera abrupta. noexcept es un contrato estricto.

¿Se pueden atrapar excepciones por valor? ¿Por qué es peligroso?

Se pueden atrapar por valor, pero el objeto se copia (object slicing). Se recomienda usar const &.

Ejemplo de código:

try { throw std::out_of_range("err"); } catch (const std::exception& e) { /* ... */ }

Errores comunes y anti-patrones

  • Uso de throw en destructores.
  • Ignorar excepciones catch(...) sin registro.
  • No especificar noexcept donde sea posible.

Ejemplo de la vida real

Caso negativo

El código lanzaba excepciones en destructores de recursos; al finalizar de manera abrupta, la pila no se desensamblaba correctamente y los recursos quedaban como fugas.

Pros:

  • Posibilidad de detección rápida de errores.

Contras:

  • Los programas a menudo terminaban con un error de terminate.

Caso positivo

Los métodos críticos fueron marcados como noexcept, los destructores manejaban todas las excepciones internamente. Se usaban catch por referencia, y todos los errores se registraban.

Pros:

  • Fiabilidad, ausencia de fugas.
  • Comportamiento extremadamente predecible.

Contras:

  • Mayor rigurosidad para el desarrollador al escribir código correcto y "limpio".