Habla sobre los desafíos de lanzar excepciones en constructores y destructores.
sobes.tech AI
Respuesta de la IA
Llamar a una excepción en el constructor puede provocar fugas de recursos si parte de los objetos o recursos dentro del constructor se asignaron con éxito antes de que se lanzara la excepción, y el destructor correspondiente no se llame. Para prevenir fugas, se recomienda usar RAII (Adquisición de Recursos es Inicialización), por ejemplo, con punteros inteligentes o clases envolventes que garantizan liberar recursos al salir del ámbito.
Llamar a una excepción en el destructor conduce a un comportamiento indefinido. Si el destructor se llama como resultado de otra excepción (desenrollado de pila), y este mismo lanza una excepción no manejada, el programa terminará llamando a std::terminate. Según el estándar de C++, los destructores deben ser noexcept.
#include <iostream>
#include <memory> // Para std::unique_ptr
struct Resource {
Resource() { std::cout << "Recurso adquirido\n"; }
~Resource() { std::cout << "Recurso liberado\n"; }
};
struct BadConstructor {
Resource res; // Recurso asignado antes de lanzar la excepción
BadConstructor() {
std::cout << "Constructor BadConstructor\n";
throw std::runtime_error("Error en el constructor"); // Lanzamiento de excepción
// El destructor de Resource no será llamado si la excepción se lanza antes de completar el constructor
}
~BadConstructor() noexcept { // Los destructores deben ser noexcept
std::cout << "Destructor BadConstructor\n";
}
};
struct GoodConstructor {
std::unique_ptr<Resource> res; // Uso de puntero inteligente para RAII
GoodConstructor() : res(std::make_unique<Resource>()) {
std::cout << "Constructor GoodConstructor\n";
// Ahora, si se lanza una excepción, unique_ptr garantiza llamar al destructor de Resource al salir del scope
// throw std::runtime_error("Error en el constructor");
}
~GoodConstructor() noexcept {
std::cout << "Destructor GoodConstructor\n";
}
};
struct BadDestructor {
BadDestructor() { std::cout << "Constructor BadDestructor\n"; }
~BadDestructor() {
std::cout << "Destructor BadDestructor\n";
// No se debe lanzar excepción desde un destructor, especialmente durante el desenrollado
// throw std::runtime_error("Error en el destructor");
}
};
int main() {
// Ejemplo con constructor (llevará a fuga de Resource sin RAII)
try {
// BadConstructor bc; // Descomentar para demostrar fuga
} catch (const std::exception& e) {
std::cout << "Excepción atrapada: " << e.what() << "\n";
}
// Ejemplo con buen constructor (usa RAII)
try {
// GoodConstructor gc; // Descomentar para demostrar, no habrá fuga
} catch (const std::exception& e) {
std::cout << "Excepción atrapada: " << e.what() << "\n";
}
// Ejemplo con destructor (puede llevar a std::terminate)
try {
// BadDestructor bd; // Descomentar si quieres ver un terminate potencial
// throw std::runtime_error("Otra excepción"); // Excepción que causará unwind
} catch (const std::exception& e) {
std::cout << "Excepción atrapada: " << e.what() << "\n";
}
return 0;
}
Tabla resumen:
| Lugar de llamada a excepción | Problemas potenciales | Solución recomendada |
|---|---|---|
| Constructor | Fuga de recursos | Usar RAII (punteros inteligentes, envoltorios) |
| Destructor | Comportamiento indefinido, std::terminate durante unwind |
Los destructores deben ser noexcept, evitar lanzar excepciones |