Fale sobre os desafios de lançar exceções (throw exception) nos construtores e destrutores.
sobes.tech IA
Resposta da IA
Chamar uma exceção no construtor pode levar a fugas de recursos se parte dos objetos ou recursos dentro do construtor foram atribuídos com sucesso antes do lançamento da exceção, e o destruidor correspondente não for chamado. Para evitar fugas, recomenda-se usar RAII (Aquisição de Recursos é Inicialização), por exemplo, com ponteiros inteligentes ou classes envolventes que garantem liberar recursos ao sair do escopo.
Chamar uma exceção no destruidor leva a um comportamento indefinido. Se o destruidor for chamado como resultado de outra exceção (desenrolar da pilha), e este lançar uma exceção não tratada, o programa terminará chamando std::terminate. De acordo com o padrão C++, os destrutores devem 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 será alocado antes do lançamento da exceção
BadConstructor() {
std::cout << "Construtor BadConstructor\n";
throw std::runtime_error("Erro no construtor"); // Lançamento de exceção
// O destruidor de Resource não será chamado se a exceção for lançada antes do final do construtor
}
~BadConstructor() noexcept { // Os destrutores devem ser noexcept
std::cout << "Destruidor BadConstructor\n";
}
};
struct GoodConstructor {
std::unique_ptr<Resource> res; // Uso de ponteiro inteligente para RAII
GoodConstructor() : res(std::make_unique<Resource>()) {
std::cout << "Construtor GoodConstructor\n";
// Agora, se lançar uma exceção, o unique_ptr garante a chamada ao destruidor de Resource ao sair do escopo
// throw std::runtime_error("Erro no construtor");
}
~GoodConstructor() noexcept {
std::cout << "Destruidor GoodConstructor\n";
}
};
struct BadDestructor {
BadDestructor() { std::cout << "Construtor BadDestructor\n"; }
~BadDestructor() {
std::cout << "Destruidor BadDestructor\n";
// Não se deve lançar exceções de um destruidor, especialmente durante o unwind
// throw std::runtime_error("Erro no destruidor");
}
};
int main() {
// Exemplo com construtor (levará a fuga de Resource sem RAII)
try {
// BadConstructor bc; // Descomentar para demonstração de fuga
} catch (const std::exception& e) {
std::cout << "Exceção capturada: " << e.what() << "\n";
}
// Exemplo com bom construtor (usa RAII)
try {
// GoodConstructor gc; // Descomentar para demonstração, não haverá fuga
} catch (const std::exception& e) {
std::cout << "Exceção capturada: " << e.what() << "\n";
}
// Exemplo com destruidor (pode levar a std::terminate)
try {
// BadDestructor bd; // Descomentar se desejar ver um terminate potencial
// throw std::runtime_error("Outra exceção"); // Exceção que causará unwind
} catch (const std::exception& e) {
std::cout << "Exceção capturada: " << e.what() << "\n";
}
return 0;
}
Tabela resumida:
| Local de chamada da exceção | Problemas potenciais | Solução recomendada |
|---|---|---|
| Construtor | Fuga de recursos | Usar RAII (ponteiros inteligentes, wrappers) |
| Destruidor | Comportamento indefinido, std::terminate durante unwind |
Os destrutores devem ser noexcept, evitar lançar exceções |