Pergunta

No C ++, você pode especificar que uma função pode ou não fazer 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

Duiverse -me realmente usá -los por causa do seguinte:

  1. O compilador realmente não aplica especificadores de exceção de maneira rigorosa, portanto os benefícios não são ótimos. Idealmente, você gostaria de obter um erro de compilação.
  2. Se uma função viola um especificador de exceção, acho que o comportamento padrão é encerrar o programa.
  3. No vs.net, trata o arremesso (x) como arremesso (...), portanto a adesão ao padrão não é forte.

Você acha que os especificadores de exceção devem ser usados?
Responda com "sim" ou "não" e forneça alguns motivos para justificar sua resposta.

Foi útil?

Solução

Não.

Aqui estão vários exemplos do porquê:

  1. O código do modelo é impossível de escrever com especificações de exceção,

    template<class T>
    void f( T k )
    {
         T x( k );
         x.x();
    }
    

    As cópias podem jogar, a passagem do parâmetro pode jogar, e x() Pode fazer alguma exceção desconhecida.

  2. Especificações de exceção tendem a proibir a extensibilidade.

    virtual void open() throw( FileNotFound );
    

    pode evoluir para

    virtual void open() throw( FileNotFound, SocketNotReady, InterprocessObjectNotImplemented, HardwareUnresponsive );
    

    Você poderia realmente escrever isso como

    throw( ... )
    

    O primeiro não é extensível, o segundo é super ambicioso e o terceiro é realmente o que você quer dizer, quando você escreve funções virtuais.

  3. Código Legado

    Quando você escreve código que se baseia em outra biblioteca, você realmente não sabe o que pode fazer quando algo dá terrivelmente errado.

    int lib_f();
    
    void g() throw( k_too_small_exception )
    { 
       int k = lib_f();
       if( k < 0 ) throw k_too_small_exception();
    }
    

    g terá terminado, quando lib_f() joga. Este é (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, a partir da qual você pode recuperar um rastreamento de pilha, do que morrer silenciosamente/violentamente.

  4. Escreva o código que retorna erros comuns e lança em ocasiões excepcionais.

    Error e = open( "bla.txt" );
    if( e == FileNotFound )
        MessageUser( "File bla.txt not found" );
    if( e == AccessDenied )
        MessageUser( "Failed to open bla.txt, because we don't have read rights ..." );
    if( e != Success )
        MessageUser( "Failed due to some other error, error code = " + itoa( e ) );
    
    try
    {
       std::vector<TObj> k( 1000 );
       // ...
    }
    catch( const bad_alloc& b )
    { 
       MessageUser( "out of memory, exiting process" );
       throw;
    }
    

No entanto, quando sua biblioteca apenas joga suas próprias exceções, você pode usar as especificações de exceção para declarar sua intenção.

Outras dicas

Evite especificações de exceção em C ++. As razões que você dá em sua pergunta são um bom começo para o porquê.

Veja Herb Sutter "Uma visão pragmática das especificações de exceção".

Eu acho que a convenção padrão, exceto (para C ++)
Os especificadores de exceção foram um experimento no padrão C ++ que falhou principalmente.
A exceção é que o especificador de não arremesso é útil, mas você também deve adicionar o bloco de tentativa apropriado internamente para garantir que o código corresponda ao especificador. Herb Sutter tem uma página sobre o assunto. Gotch 82

Em uma adição, acho que vale a pena descrever garantias de exceção.

Estes são basicamente documentação sobre como o estado de um objeto é afetado por exceções que escapam de um método nesse objeto. Infelizmente, eles não são aplicados ou mencionados pelo compilador.
Boost e exceções

Garantias de exceção

Nenhuma garantia:

Não há garantia sobre o estado do objeto após uma exceção escapar de um método
Nessas situações, o objeto não deve mais ser usado.

Garantia básica:

Em quase todas as situações, essa deve ser a garantia mínima que um método fornece.
Isso garante que o estado do objeto esteja bem definido e ainda pode ser usado de forma consistente.

Forte garantia: (também conhecida como garantia transacional)

Isso garante que o método será concluído com êxito
Ou uma exceção será lançada e o estado dos objetos não mudará.

Sem garantia de arremesso:

O método garante que nenhuma exceção pode se propagar do método.
Todos os destruidores devem fazer essa garantia.
| NB se uma exceção escapar de um destruidor enquanto uma exceção já está se propagando
| O aplicativo terá rescindir

O GCC emitirá avisos quando você violar as especificações de exceção. O que eu faço é usar macros para usar as especificações de exceção apenas em um modo "finco" compilar expressamente para verificar se as exceções concordam com minha documentação.

O único especificador de exceção útil é "Throw ()", como em "não arremesso".

Não. Se você os usar e uma exceção será lançada que você não especificou, seja pelo seu código ou código chamado pelo seu código, o comportamento padrão será prontamente encerrar seu programa.

Além disso, acredito que o uso deles foi depreciado nos rascunhos atuais do padrão C ++ 0x.

As especificações de exceção não são ferramentas maravilhosamente úteis no C ++. No entanto, existe / é / um bom uso para eles, se combinado com o STD :: inesperado.

O que faço em alguns projetos é o código com especificações de exceção e, em seguida, ligue para set_unexpected () com uma função que lançará uma exceção especial do meu próprio design. Essa exceção, após a construção, recebe um backtrace (de maneira específica da plataforma) e é derivada do std :: bad_exception (para permitir que seja propagado, se desejado). Se causar uma chamada de términamento (), como normalmente, o backtrace é impresso pelo que () (assim como a exceção original que o causou; não é difícil de encontrar isso) e, assim, recebo informações de onde meu contrato estava violado, como o que a exceção inesperada da biblioteca foi lançada.

Se eu fizer isso, nunca permito a propagação das exceções da biblioteca (exceto as DST) e derivar todas as minhas exceções da exceção STD ::. Se uma biblioteca decidir jogar, vou pegar e converter em minha própria hierarquia, permitindo que eu sempre controlasse o código. Funções modelo que chamam as funções dependentes devem evitar especificações de exceção por razões óbvias; Mas é raro ter uma interface de função modelo com o código da biblioteca de qualquer maneira (e poucas bibliotecas realmente usam modelos de maneira útil).

Se você está escrevendo código que será usado por pessoas que preferem olhar para a declaração de função do que qualquer comentário em torno dele, uma especificação dirá a eles quais exceções elas podem querer capturar.

Caso contrário, não acho particularmente útil usar nada além de throw() para indicar que não joga exceções.

Uma especificação "Throw ()" permite que o compilador execute algumas otimizações ao fazer análise de fluxo de código se souber que essa 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

Geralmente eu não usaria especificadores de exceção. No entanto, nos casos em que se alguma outra exceção viesse da função em questão que o programa seria definitivamente incapaz de correto, então pode 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 sendo jogada de uma função com especificadores de exceção é chamar termine ().

Também notarei que Scott Meyers aborda esse assunto em C ++ mais eficaz. Seu C ++ eficaz e C ++ mais eficazes são livros altamente recomendados.

Sim, se você gosta de documentação interna. Ou talvez escrevendo um Libary que outras pessoas usarão, para que 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.

Eu concordo, eles não são realmente úteis para aplicar o estilo Java no compilador, mas é melhor do que nada ou comentários aleatórios.

Eles podem ser úteis para testes de unidade para que, ao escrever testes, você saiba o que esperar que a função jogue quando falhar, mas não há fiscalização em torno deles no compilador. Eu acho que eles são um código extra que não é necessário no C ++. O que você escolhe tudo o que deve ter certeza é que segue o mesmo padrão de codificação em todo o projeto e nos membros da equipe para que seu código permaneça legível.

Do artigo:

http://www.boost.org/community/exception_safety.html

"É conhecido por ser impossível escrever um contêiner genérico seguro para exceção". Essa reivindicação é frequentemente ouvida com referência a um artigo de Tom Cargill [4], no qual ele explora o problema de segurança de exceção para um modelo de pilha genérica. Em seu artigo, Cargill levanta muitas questões úteis, mas infelizmente não apresenta uma solução para o seu problema.1 Ele conclui sugerindo que uma solução pode não ser possível. Infelizmente, seu artigo foi lido por muitos como "prova" dessa especulação. Desde que foi publicado, houve muitos exemplos de componentes genéricos à prova de exceção, entre eles os contêineres da biblioteca padrão C ++.

E, de fato, posso pensar em maneiras de fazer a exceção das classes de modelo. A menos que você não tenha controle sobre todas as subclasses, poderá ter um problema de qualquer maneira. Para fazer isso, pode criar typedefs em suas classes que definem as exceções lançadas por várias classes de modelo. Esse pensamento que o problema está sempre enfrentando -o depois, em vez de projetá -lo desde o início, e acho que é essa sobrecarga que é o verdadeiro obstáculo.

Especificações de exceção = lixo, pergunte a qualquer desenvolvedor Java com mais de 30 anos

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top