Tratamento Avançado de Erros em C++: do try/catch às Práticas Industriais
O tratamento de erros é uma das pedras angulares da programação industrial. Em C++, o desenvolvedor tem acesso não apenas ao mecanismo clássico try/catch, mas também a todo um arsenal de técnicas avançadas: desde RAII até garantias de segurança de exceções. Neste artigo, vamos analisar como escrever código C++ robusto que lida corretamente com falhas, não vaza recursos e permanece legível.
1. Fundamentos e Armadilhas Comuns do try/catch
A sintaxe básica é familiar para todos:
#include <iostream>#include <stdexcept>
void dangerousFunction(int value) { if (value < 0) { throw std::invalid_argument("Value must be non-negative"); } // ... trabalho com value}
int main() { try { dangerousFunction(-5); } catch (const std::invalid_argument& e) { std::cerr << "Error: " << e.what() << std::endl; } catch (const std::exception& e) { std::cerr << "Generic error: " << e.what() << std::endl; } catch (...) { std::cerr << "Unknown exception caught" << std::endl; } return 0;}Pontos importantes:
- Capture por referência —
catch (const std::exception& e)evita o slicing de objetos. - A ordem dos blocos catch é importante: primeiro exceções mais específicas, depois as gerais.
- Não abuse de
catch (...)— ele esconde todos os erros, incluindo os críticos (por exemplo,std::bad_alloc). - Nunca lance exceções de destrutores — isso leva a comportamento indefinido (geralmente a uma terminação abrupta).
2. RAII como Alternativa ao try/catch para Gerenciamento de Recursos
RAII (Resource Acquisition Is Initialization) — o padrão chave do C++ que torna as exceções seguras. A ideia: o recurso é adquirido no construtor e liberado no destrutor. Se ocorrer uma exceção, os destrutores de todos os objetos criados são chamados automaticamente (stack unwinding).
#include <fstream>#include <vector>#include <stdexcept>
void processFile(const std::string& filename) { std::ifstream file(filename); // RAII: o arquivo será aberto no construtor if (!file.is_open()) { throw std::runtime_error("Cannot open file: " + filename); }
std::vector<int> data; int value; while (file >> value) { data.push_back(value); } // O destrutor de file fechará o arquivo automaticamente, // mesmo se houver uma exceção (por exemplo, bad_alloc no push_back)}Vantagens do RAII sobre o try/catch manual:
- Não é necessário escrever blocos
finally— os destrutores garantem a limpeza. - O código se torna mais linear e legível.
- Exceções não levam a vazamentos de memória, descritores de arquivo, etc.
3. Garantias de Segurança de Exceções
Em C++, costuma-se distinguir três níveis de garantia para funções:
- No-throw guarantee — a função nunca lança exceções. Marcada com
noexcept. - Strong guarantee — se ocorrer uma exceção, o estado do programa permanece inalterado (operação atômica).
- Basic guarantee — na ocorrência de uma exceção, não há vazamento de recursos, os objetos permanecem em um estado consistente, mas seu valor pode mudar.
Exemplo com strong guarantee (idioma copy-and-swap):
#include <algorithm> // para std::swap#include <vector>#include <stdexcept>
class SafeVector { std::vector<int> data_;public: void push_back(int value) { // 1. Cria