No C ++, você pode especificar que uma função pode ou não lançar uma exceção usando um especificador de exceção. Por exemplo:
void foo() throw(); // guaranteed not to throw an exception
void bar() throw(int); // may throw an exception of type int
void baz() throw(...); // may throw an exception of some unspecified type
Eu tenho dúvidas sobre realmente usá-los devido ao seguinte:
- O compilador não aplica os especificadores de exceção de maneira rigorosa, portanto os benefícios não são grandes. Idealmente, você gostaria de receber um erro de compilação.
- Se uma função viola um especificador de exceção, acho que o comportamento padrão é finalizar o programa.
- No VS.Net, trata throw (X) como throw (...), portanto, a adesão ao padrão não é forte.
Você acha que especificadores de exceção devem ser usados?
Por favor, responda com "sim" ou "não" e forneça alguns motivos para justificar sua resposta.
Respostas:
Não.
Aqui estão vários exemplos dos motivos:
É impossível escrever o código do modelo com especificações de exceção,
As cópias podem ser lançadas, a passagem de parâmetros pode ser lançada e
x()
pode gerar alguma exceção desconhecida.As especificações de exceção tendem a proibir a extensibilidade.
pode evoluir para
Você poderia realmente escrever isso como
O primeiro não é extensível, o segundo é ambicioso e o terceiro é realmente o que você quer dizer quando escreve funções virtuais.
Código legado
Quando você escreve um código que se baseia em outra biblioteca, não sabe realmente o que ele pode fazer quando algo der terrivelmente errado.
g
terminará quandolib_f()
jogar. Isso (na maioria dos casos) não é o que você realmente deseja.std::terminate()
nunca deve ser chamado. É sempre melhor deixar o aplicativo travar com uma exceção não tratada, da qual você pode recuperar um rastreamento de pilha, do que morrer silenciosa / violentamente.Escreva um código que retorne erros comuns e jogue em ocasiões excepcionais.
No entanto, quando sua biblioteca apenas lança suas próprias exceções, você pode usar as especificações de exceção para declarar sua intenção.
fonte
try {...} catch (<specified exceptions>) { <do whatever> } catch (...) { unexpected(); ]
construção, independentemente de você querer ou não um bloco de tentativa.try { <<...code...>> } catch(...) /* stack guaranteed to be unwound here and dtors run */ { throw; /* pass it on to the runtime */ }
terminate()
? Por que não basta ligarabort()
?Evite especificações de exceção em C ++. As razões apresentadas na sua pergunta são um bom começo para o porquê.
Veja "Um olhar pragmático sobre especificações de exceções", de Herb Sutter .
fonte
throw(optional-type-id-list)
) está obsoleto no C ++ 11. Eles ainda estão no padrão, mas acho que foi enviada uma advertência de que seu uso deve ser considerado com cuidado. C ++ 11 adiciona anoexcept
especificação e o operador. Não sei detalhes suficientesnoexcept
para comentar. Este artigo parece ser bastante detalhado: akrzemi1.wordpress.com/2011/06/10/using-noexcept E Dietmar Kühl tem um artigo no Overload Journal de junho de 2011: accu.org/var/uploads/journals/overload103.pdfthrow(something)
é considerado inútil e uma má ideia.throw()
é útil.Eu acho que os
especificadores de exceção convencionalmente, exceto convenção (para C ++), foram um experimento no padrão C ++ que falhou principalmente.
A exceção é que o especificador no throw é útil, mas você também deve adicionar internamente o bloco try try apropriado, para garantir que o código corresponda ao especificador. Herb Sutter tem uma página sobre o assunto. Gotch 82
Além disso, acho que vale a pena descrever as garantias de exceção.
Estas são basicamente a documentação de como o estado de um objeto é afetado por exceções que escapam de um método nesse objeto. Infelizmente, eles não são impostos ou mencionados pelo compilador.
Impulso e exceções
Garantias de exceção
Nenhuma garantia:
Garantia básica:
Garantia Forte: (aka Garantia Transacional)
Nenhuma garantia do lance:
fonte
Guarantee that functions will only throw listed exceptions (possibly none)
. Não é verdade. Ele garante apenas que, se a função lançar essas exceções, o aplicativo será encerrado.Enable compiler optimizations based on the knowledge that only listed exceptions (possibly none) will be thrown
Não pode fazer isso. Como acabei de salientar, você não pode garantir que a exceção não será lançada.throw()
(não lança). " Ele garante apenas que, se a função lançar essas exceções, o aplicativo encerrará " Não "não verdadeiro" significa verdadeiro. Não há garantia em C ++ de que uma função nunca chameterminate()
. " Como acabei de salientar, você não pode garantir que a exceção não será lançada " Você garante que a função não será lançada . Qual é exatamente o que você precisa.O gcc emitirá avisos quando você violar as especificações de exceção. O que faço é usar macros para usar as especificações de exceção apenas no modo "fiapo", compilado expressamente para verificar se as exceções estão de acordo com a minha documentação.
fonte
O único especificador de exceção útil é "throw ()", como em "não lança".
fonte
As especificações de exceção não são ferramentas maravilhosamente úteis em C ++. No entanto, existe / há um bom uso para eles, se combinado com std :: inesperado.
O que faço em alguns projetos é codificar com especificações de exceção e, em seguida, chamar set_unexpected () com uma função que lançará uma exceção especial do meu próprio design. Essa exceção, na construção, obtém um retorno (de maneira específica da plataforma) e é derivada de std :: bad_exception (para permitir que ela seja propagada, se desejado). Se causar uma chamada terminate (), como geralmente ocorre, o backtrace será impresso com o que () (bem como com a exceção original que a causou; não é difícil encontrá-la) e, assim, recebo informações de onde estava meu contrato violado, como qual exceção inesperada da biblioteca foi lançada.
Se fizer isso, nunca permito a propagação de exceções de biblioteca (exceto as padrão) e derivo todas as minhas exceções de std :: exception. Se uma biblioteca decidir lançar, vou capturar e converter em minha própria hierarquia, permitindo que eu sempre controle o código. Funções modeladas que chamam funções dependentes devem evitar especificações de exceção por razões óbvias; mas é raro ter uma interface de função modelo com código de biblioteca de qualquer maneira (e poucas bibliotecas realmente usam modelos de uma maneira útil).
fonte
Se você estiver escrevendo um código que será usado por pessoas que preferem olhar para a declaração de função do que por comentários, uma especificação informará quais exceções eles podem querer capturar.
Caso contrário, não acho particularmente útil usar nada além
throw()
de indicar que ele não gera nenhuma exceção.fonte
Não. Se você usá-los e for lançada uma exceção que você não especificou, seja pelo seu código ou código chamado pelo seu código, o comportamento padrão é encerrar imediatamente o seu programa.
Além disso, acredito que seu uso foi descontinuado nos rascunhos atuais do padrão C ++ 0x.
fonte
Uma especificação "throw ()" permite que o compilador realize algumas otimizações ao fazer uma análise de fluxo de código se souber que a função nunca lançará uma exceção (ou pelo menos promete nunca lançar uma exceção). Larry Osterman fala sobre isso brevemente aqui:
http://blogs.msdn.com/larryosterman/archive/2006/03/22/558390.aspx
fonte
Geralmente eu não usaria especificadores de exceção. No entanto, nos casos em que qualquer outra exceção viesse da função em questão, o programa seria definitivamente incapaz de corrigir , ela poderá ser útil. Em todos os casos, certifique-se de documentar claramente quais exceções poderiam ser esperadas dessa função.
Sim, o comportamento esperado de uma exceção não especificada lançada de uma função com especificadores de exceção é chamar terminate ().
Também observarei que Scott Meyers aborda esse assunto em C ++ mais eficaz. Seu C ++ eficaz e C ++ mais eficaz são livros altamente recomendados.
fonte
Sim, se você estiver na documentação interna. Ou talvez escrevendo uma biblioteca que outros usem, para que eles possam dizer o que acontece sem consultar a documentação. Jogar ou não jogar pode ser considerado parte da API, quase como o valor de retorno.
Concordo que eles não são realmente úteis para impor a correção do estilo Java no compilador, mas é melhor do que nada ou comentários aleatórios.
fonte
Eles podem ser úteis para testes de unidade, para que, ao escrever os testes, você saiba o que esperar da função quando ela falhar, mas não haverá aplicação ao redor deles no compilador. Eu acho que eles são códigos extras que não são necessários em C ++. Seja qual for a escolha de tudo, você deve ter certeza de que segue o mesmo padrão de codificação no projeto e nos membros da equipe para que seu código permaneça legível.
fonte
Do artigo:
http://www.boost.org/community/exception_safety.html
E, de fato, consigo pensar em maneiras de tornar as classes de modelos seguras. A menos que você não tenha controle sobre todas as subclasses, poderá ter um problema de qualquer maneira. Para fazer isso, você pode criar typedefs em suas classes que definem as exceções geradas por várias classes de modelos. Acho que o problema é sempre resolvê-lo depois, em vez de projetá-lo desde o início, e acho que é essa sobrecarga que é o verdadeiro obstáculo.
fonte
Especificações de exceção = lixo, pergunte a qualquer desenvolvedor Java com mais de 30 anos
fonte