Embora eu concorde que pegar ...
sem reler é realmente errado, no entanto, acredito que o uso de construções como esta:
try
{
// Stuff
}
catch (...)
{
// Some cleanup
throw;
}
É aceitável nos casos em que o RAII não é aplicável . (Por favor, não pergunte ... nem todo mundo na minha empresa gosta de programação orientada a objetos e o RAII é frequentemente visto como "material escolar inútil" ...)
Meus colegas de trabalho dizem que você sempre deve saber quais exceções devem ser lançadas e que sempre pode usar construções como:
try
{
// Stuff
}
catch (exception_type1&)
{
// Some cleanup
throw;
}
catch (exception_type2&)
{
// Some cleanup
throw;
}
catch (exception_type3&)
{
// Some cleanup
throw;
}
Existe uma boa prática bem admitida em relação a essas situações?
...
", enquanto meu foco pergunta sobre "Devo melhor captura...
ou<specific exception>
antes rethrowing"main
,catch(...) { return EXIT_FAILURE; }
pode estar correto no código que não está sendo executado em um depurador. Se você não pegar, a pilha pode não ser desenrolada. É somente quando seu depurador detecta exceções não capturadas que você deseja que ele saiamain
.Respostas:
Seu colega de trabalho, detestaria dizer, obviamente nunca trabalhou em bibliotecas de uso geral.
Como no mundo uma classe pode
std::vector
até fingir saber o que os construtores de cópias lançarão, garantindo a segurança das exceções?Se você sempre soubesse o que o receptor faria em tempo de compilação, o polimorfismo seria inútil! Às vezes, o objetivo inteiro é abstrair o que acontece em um nível inferior, para que você não queira saber o que está acontecendo!
fonte
...
?O que você parece ser pego é o inferno específico de alguém tentando comer seu bolo e também comê-lo.
RAII e exceções são projetadas para andar de mãos dadas. RAII é o meio pelo qual você não precisa escrever muitas
catch(...)
instruções para fazer a limpeza. Isso acontecerá automaticamente, como é óbvio. E as exceções são a única maneira de trabalhar com objetos RAII, porque os construtores só podem ter sucesso ou lançar (ou colocar o objeto em um estado de erro, mas quem quer isso?).Uma
catch
declaração pode fazer uma de duas coisas: lidar com um erro ou circunstância excepcional ou executar um trabalho de limpeza. Às vezes, ele faz as duas coisas, mascatch
existe toda declaração para fazer pelo menos uma delas.catch(...)
é incapaz de executar um tratamento de exceção adequado. Você não sabe qual é a exceção; você não pode obter informações sobre a exceção. Você não tem absolutamente nenhuma informação além do fato de uma exceção ter sido lançada por algo dentro de um determinado bloco de código. A única coisa legítima que você pode fazer nesse bloco é fazer a limpeza. E isso significa repetir a exceção no final da limpeza.O que RAII oferece a você em relação ao tratamento de exceções é a limpeza gratuita. Se tudo estiver encapsulado corretamente em RAII, tudo será limpo corretamente. Você não precisa mais ter
catch
instruções para limpar. Nesse caso, não há razão para escrever umacatch(...)
declaração.Então, eu concordo que isso
catch(...)
é principalmente mau ... provisoriamente .Essa disposição é o uso adequado da RAII. Porque sem ele, você precisa ser capaz de fazer determinada limpeza. Não há como fugir disso; você precisa ser capaz de fazer o trabalho de limpeza. Você precisa garantir que a emissão de uma exceção deixe o código em um estado razoável. E
catch(...)
é uma ferramenta vital para isso.Você não pode ter um sem o outro. Você não pode dizer que ambos RAII e
catch(...)
são ruins. Você precisa de pelo menos um deles; caso contrário, você não será seguro para exceções.Claro, há um uso válido, embora raro,
catch(...)
que nem a RAII pode banir:exception_ptr
encaminhar para outra pessoa. Normalmente através de umapromise/future
interface ou similar.Seu colega de trabalho é um idiota (ou apenas terrivelmente ignorante). Isso deve ser imediatamente óbvio devido à quantidade de código de copiar e colar que ele sugere que você escreva. A limpeza para cada uma dessas instruções de captura será exatamente a mesma . Isso é um pesadelo de manutenção, sem mencionar a legibilidade.
Resumindo: esse é o problema que o RAII foi criado para resolver (não que ele não resolva outros problemas).
O que me confunde sobre essa noção é que geralmente é o contrário de como a maioria das pessoas argumenta que a RAII é ruim. Geralmente, o argumento diz "RAII é ruim porque você precisa usar exceções para sinalizar falha no construtor. Mas você não pode lançar exceções, porque não é seguro e terá que ter muitas
catch
instruções para limpar tudo". O que é um argumento quebrado, porque o RAII resolve o problema que a falta de RAII cria.Provavelmente, ele é contra a RAII porque esconde detalhes. As chamadas de destruidor não são visíveis imediatamente nas variáveis automáticas. Então você obtém um código chamado implicitamente. Alguns programadores realmente odeiam isso. Aparentemente, até o ponto em que eles acham que ter três
catch
instruções, todas as quais fazem o mesmo com o código de copiar e colar, é uma idéia melhor.fonte
Dois comentários, sério. A primeira é que, enquanto em um mundo ideal, você sempre deve saber quais exceções podem ser lançadas, na prática, se estiver lidando com bibliotecas de terceiros ou compilando com um compilador da Microsoft, não sabe. Mais ao ponto, no entanto; mesmo se você souber exatamente todas as possíveis exceções, isso é relevante aqui?
catch (...)
expressa a intenção muito melhor do quecatch ( std::exception const& )
, mesmo supondo que todas as exceções possíveis derivem destd::exception
(o que seria o caso em um mundo ideal). Quanto ao uso de vários blocos de captura, se não houver uma base comum para todas as exceções: isso é ofuscação total e um pesadelo de manutenção. Como você reconhece que todos os comportamentos são idênticos? E essa era essa a intenção? E o que acontece se você precisar alterar o comportamento (correção de bug, por exemplo)? É muito fácil perder um.fonte
std::exception
e tenta todos os dias impor seu uso entre nossa base de código. Meu palpite é que ele tenta me punir por usar código e bibliotecas externas que ele não escreveu.std::vector<>
pode lançar qualquer tipo de exceção por praticamente qualquer motivo.Acho que seu colega de trabalho confundiu alguns bons conselhos - você só deve lidar com exceções conhecidas em um
catch
bloco quando não as repetir.Isso significa:
É ruim porque oculta silenciosamente qualquer erro.
Contudo:
Está bom - sabemos com o que estamos lidando e não precisamos expô-lo ao código de chamada.
Da mesma forma:
É bom, mesmo prática recomendada, o código para lidar com erros gerais deve estar com o código que os causa. É melhor do que confiar no chamado para saber que uma transação precisa ser revertida ou o que for.
fonte
Qualquer resposta de sim ou não deve ser acompanhada de uma justificativa do motivo.
Dizer que está errado, simplesmente porque me ensinaram dessa maneira, é apenas fanatismo cego.
Escrever o mesmo
//Some cleanup; throw
várias vezes, como no seu exemplo, está errado porque é a duplicação de código e isso é uma carga de manutenção. Escrever apenas uma vez é melhor.Escrever um
catch(...)
para silenciar todas as exceções está errado, porque você deve lidar apenas com as exceções que você sabe como lidar, e com esse curinga você pode fazer mais do que o esperado, e isso pode silenciar erros importantes.Mas se você repetir
catch(...)
novamente após a , a última lógica não se aplicará mais, pois você não está realmente lidando com a exceção; portanto, não há razão para que isso seja desencorajado.Na verdade, eu fiz isso para efetuar logon em funções sensíveis sem nenhum problema:
fonte
Log(...)
que não possa jogar.Eu geralmente concordo com o humor das postagens aqui, eu realmente não gosto do padrão de capturar exceções específicas - acho que a sintaxe disso ainda está em sua infância e ainda não é capaz de lidar com o código redundante.
Mas como todo mundo está dizendo isso, vou me deparar com o fato de que, embora eu os use com moderação, muitas vezes olhei para uma das minhas declarações "catch (Exceção e)" e disse "Droga, eu gostaria de ter chamado fora as exceções específicas dessa época "porque, quando você chega mais tarde, geralmente é bom saber qual era a intenção e o que o cliente provavelmente lançaria de relance.
Não estou justificando a atitude de "Sempre use x", apenas dizendo que ocasionalmente é bom vê-los listados e tenho certeza que é por isso que algumas pessoas pensam que esse é o caminho "certo" a seguir.
fonte