Você (realmente) escreve um código de exceção seguro? [fechadas]


312

O tratamento de exceções (EH) parece ser o padrão atual e, pesquisando na Web, não consigo encontrar novas idéias ou métodos que tentem melhorar ou substituí-lo (bem, existem algumas variações, mas nada de novo).

Embora a maioria das pessoas pareça ignorá-lo ou simplesmente aceitá-lo, o EH tem algumas desvantagens enormes: as exceções são invisíveis para o código e ele cria muitos e muitos pontos de saída possíveis. Joel on software escreveu um artigo sobre isso . A comparação com os gotoajustes perfeitos, me fez pensar novamente sobre EH.

Tento evitar o EH e apenas uso valores de retorno, retornos de chamada ou o que for mais adequado ao objetivo. Mas quando você precisa escrever um código confiável, não pode ignorar o EH atualmente : ele começa com o new, que pode gerar uma exceção, em vez de retornar 0 (como nos velhos tempos). Isso torna qualquer linha de código C ++ vulnerável a uma exceção. E mais lugares no código fundamental do C ++ lançam exceções ... std lib faz isso e assim por diante.

É como andar em terrenos instáveis . Então, agora somos forçados a cuidar de exceções!

Mas é difícil, é realmente difícil. Você precisa aprender a escrever um código de exceção seguro e, mesmo que tenha alguma experiência com ele, ainda será necessário verificar novamente qualquer linha de código para ser segura! Ou você começa a colocar blocos try / catch em todos os lugares, o que confunde o código até que ele atinja um estado de ilegibilidade.

O EH substituiu a antiga abordagem determinística limpa (valores de retorno ..), que tinha apenas algumas desvantagens, mas compreensíveis e facilmente solucionáveis, por uma abordagem que cria muitos pontos de saída possíveis no seu código e se você começar a escrever código que capte exceções (o que você são obrigados a fazê-lo em algum momento), ele até cria uma infinidade de caminhos através do seu código (código nos blocos de captura, pense em um programa de servidor no qual você precisa de recursos de registro diferentes de std :: cerr ..). EH tem vantagens, mas esse não é o ponto.

Minhas perguntas reais:

  • Você realmente escreve código de exceção seguro?
  • Tem certeza de que seu último código "pronto para produção" é seguro contra exceções?
  • Você pode ter certeza disso?
  • Você conhece e / ou realmente usa alternativas que funcionam?

44
"O EH substituiu a antiga abordagem determinística limpa (valores de retorno ..)" O quê? As exceções são tão determinísticas quanto os códigos de retorno. Não há nada aleatório sobre eles.
Rbond

2
EH tem sido o padrão por um longo tempo (desde Ada mesmo!)

12
"EH" é uma abreviação estabelecida para tratamento de exceções? Eu nunca vi isso antes.
Thomas Padron-McCarthy

3
As leis de Newton são aplicáveis ​​hoje para a maioria das coisas. O fato de terem previsto uma gama muito ampla de fenômenos por séculos é significativo.
David Thornley

4
@frunsi: Heh, desculpe por confundir você! Mas acho que, se alguma coisa, um acrônimo ou abreviação estranho e inexplicável desencorajará as pessoas mais do que não.
Thomas Padron-McCarthy

Respostas:


520

Sua pergunta faz uma afirmação de que "Escrever código com exceção de segurança é muito difícil". Vou responder às suas perguntas primeiro e depois responder à pergunta oculta por trás delas.

Respondendo a perguntas

Você realmente escreve código de exceção seguro?

Claro que eu faço.

Esta é a razão pela qual o Java perdeu muito de seu apelo para mim como programador C ++ (falta de semântica RAII), mas estou discursando: Esta é uma pergunta do C ++.

Na verdade, é necessário quando você precisa trabalhar com o código STL ou Boost. Por exemplo, os threads C ++ ( boost::threadou std::thread) lançam uma exceção para sair normalmente.

Tem certeza de que seu último código "pronto para produção" é seguro contra exceções?

Você pode ter certeza disso?

Escrever código com exceção de segurança é como escrever código sem erros.

Você não pode ter 100% de certeza de que seu código é seguro contra exceções. Mas então, você se esforça para isso, usando padrões conhecidos e evitando anti-padrões conhecidos.

Você conhece e / ou realmente usa alternativas que funcionam?

Não alternativas viáveis ​​no C ++ (ou seja, você precisará voltar ao C e evitar as bibliotecas C ++, além de surpresas externas como o Windows SEH).

Escrevendo código de exceção seguro

Para escrever um código de exceção seguro, você deve primeiro saber qual o nível de segurança de exceção de cada instrução que você escreve.

Por exemplo, um newpode lançar uma exceção, mas a atribuição de um built-in (por exemplo, um int ou um ponteiro) não falhará. Uma troca nunca falha (nunca escreva uma troca jogando), um std::list::push_backpode lançar ...

Garantia de exceção

A primeira coisa a entender é que você deve poder avaliar a garantia de exceção oferecida por todas as suas funções:

  1. none : seu código nunca deve oferecer isso. Esse código vazará tudo e será quebrado na primeira exceção lançada.
  2. básico : esta é a garantia que você deve oferecer no mínimo, ou seja, se uma exceção for lançada, nenhum recurso será vazado e todos os objetos ainda serão inteiros
  3. forte : o processamento será bem-sucedido ou gerará uma exceção, mas se for lançado, os dados estarão no mesmo estado como se o processamento não tivesse sido iniciado (isso fornece um poder transacional para C ++)
  4. nothrow / nofail : O processamento será bem-sucedido.

Exemplo de código

O código a seguir parece o C ++ correto, mas, na verdade, oferece a garantia "none" e, portanto, não está correto:

void doSomething(T & t)
{
   if(std::numeric_limits<int>::max() > t.integer)  // 1.   nothrow/nofail
      t.integer += 1 ;                              // 1'.  nothrow/nofail
   X * x = new X() ;                // 2. basic : can throw with new and X constructor
   t.list.push_back(x) ;            // 3. strong : can throw
   x->doSomethingThatCanThrow() ;   // 4. basic : can throw
}

Eu escrevo todo o meu código com esse tipo de análise em mente.

A garantia mais baixa oferecida é básica, mas a ordem de cada instrução torna a função inteira "nenhuma", porque se 3. lançar, x vazará.

A primeira coisa a fazer seria tornar a função "básica", ou seja, colocar x em um ponteiro inteligente até que ele pertença à lista com segurança:

void doSomething(T & t)
{
   if(std::numeric_limits<int>::max() > t.integer)  // 1.   nothrow/nofail
      t.integer += 1 ;                              // 1'.  nothrow/nofail
   std::auto_ptr<X> x(new X()) ;    // 2.  basic : can throw with new and X constructor
   X * px = x.get() ;               // 2'. nothrow/nofail
   t.list.push_back(px) ;           // 3.  strong : can throw
   x.release() ;                    // 3'. nothrow/nofail
   px->doSomethingThatCanThrow() ;  // 4.  basic : can throw
}

Agora, nosso código oferece uma garantia "básica". Nada vazará e todos os objetos estarão em um estado correto. Mas poderíamos oferecer mais, isto é, a forte garantia. É aqui que pode se tornar caro, e é por isso que nem todo código C ++ é forte. Vamos tentar:

void doSomething(T & t)
{
   // we create "x"
   std::auto_ptr<X> x(new X()) ;    // 1. basic : can throw with new and X constructor
   X * px = x.get() ;               // 2. nothrow/nofail
   px->doSomethingThatCanThrow() ;  // 3. basic : can throw

   // we copy the original container to avoid changing it
   T t2(t) ;                        // 4. strong : can throw with T copy-constructor

   // we put "x" in the copied container
   t2.list.push_back(px) ;          // 5. strong : can throw
   x.release() ;                    // 6. nothrow/nofail
   if(std::numeric_limits<int>::max() > t2.integer)  // 7.   nothrow/nofail
      t2.integer += 1 ;                              // 7'.  nothrow/nofail

   // we swap both containers
   t.swap(t2) ;                     // 8. nothrow/nofail
}

Reordenamos as operações, primeiro criando e configurando Xseu valor correto. Se alguma operação falhar, ela tnão será modificada e, portanto, as operações 1 a 3 poderão ser consideradas "fortes": se algo for lançado, tnão for modificado e Xnão vazar porque é de propriedade do ponteiro inteligente.

Então, criamos uma cópia t2de t, e trabalhar sobre esta cópia de operação de 4 a 7. Se algo joga, t2é modificado, mas, em seguida, tainda é o original. Ainda oferecemos a garantia forte.

Então, trocamos te t2. As operações de troca devem ser notowow em C ++, então, esperamos que a troca para a qual você escreveu não Tseja notowow (se não for, reescreva-a para que não sejaowow).

Portanto, se chegarmos ao final da função, tudo será bem-sucedido (Não há necessidade de um tipo de retorno) e tterá seu valor excedido. Se falhar, tainda terá seu valor original.

Agora, oferecer a garantia forte pode ser bastante oneroso, portanto, não se esforce para oferecer a garantia forte a todo o seu código, mas se você puder fazê-lo sem nenhum custo (o inline C ++ e outras otimizações podem tornar todo o código acima sem custo) , então faça. O usuário da função agradecerá por isso.

Conclusão

É necessário algum hábito para escrever código com exceção de segurança. Você precisará avaliar a garantia oferecida por cada instrução que usar e, em seguida, avaliar a garantia oferecida por uma lista de instruções.

Obviamente, o compilador C ++ não fará backup da garantia (no meu código, ofereço a garantia como uma tag dowargen @warning), o que é meio triste, mas não deve impedi-lo de tentar escrever código com exceção de segurança.

Falha normal vs. erro

Como um programador pode garantir que uma função sem falha sempre terá êxito? Afinal, a função pode ter um bug.

Isso é verdade. As garantias de exceção devem ser oferecidas por código sem erros. Mas então, em qualquer idioma, chamar uma função supõe que a função esteja livre de erros. Nenhum código são se protege contra a possibilidade de haver um bug. Escreva o código da melhor maneira possível e, em seguida, ofereça a garantia com a suposição de que está livre de erros. E se houver um erro, corrija-o.

As exceções são para falhas excepcionais de processamento, não para erros de código.

Últimas palavras

Agora, a pergunta é "Isso vale a pena?".

Claro que é. Ter uma função "não mostrar / não falhar" sabendo que a função não falhará é um grande benefício. O mesmo pode ser dito para uma função "forte", que permite escrever código com semântica transacional, como bancos de dados, com recursos de confirmação / reversão, sendo a confirmação a execução normal do código, lançando exceções como a reversão.

Então, o "básico" é a garantia mínima que você deve oferecer. O C ++ é uma linguagem muito forte lá, com seus escopos, permitindo evitar vazamentos de recursos (algo que um coletor de lixo acharia difícil oferecer para o banco de dados, conexão ou identificadores de arquivo).

Assim, tanto quanto eu vê-lo, é pena.

Edit 29-01-2010: Sobre a troca não lançada

nobar fez um comentário que acredito ser bastante relevante, porque faz parte de "como você escreve um código de exceção seguro":

  • [me] Uma troca nunca falha (nem escreva uma troca jogando)
  • [nobar] Esta é uma boa recomendação para swap()funções personalizadas por escrito . Note-se, no entanto, que std::swap()pode falhar com base nas operações que utiliza internamente

o padrão std::swapfará cópias e atribuições que, para alguns objetos, podem ser lançadas. Assim, a troca padrão poderia ser lançada, usada para suas classes ou mesmo para classes STL. No que diz respeito ao padrão C ++, a operação de troca para vector, dequee listnão será lançada, ao contrário do que poderia acontecer mapse o functor de comparação pudesse lançar na construção de cópias (consulte A linguagem de programação C ++, edição especial, apêndice E, E.4.3 .Swap ).

Observando a implementação do Visual C ++ 2008 da troca do vetor, a troca do vetor não será lançada se os dois vetores tiverem o mesmo alocador (por exemplo, o caso normal), mas fará cópias se eles tiverem alocadores diferentes. E, portanto, suponho que poderia ser lançado neste último caso.

Portanto, o texto original ainda é válido: nunca escreva uma troca de lançamento, mas o comentário de nobar deve ser lembrado: verifique se os objetos que você está trocando têm uma troca não de lançamento.

Edit 06-11-2011: artigo interessante

Dave Abrahams , que nos deu as garantias básicas / fortes / de não comparência , descreveu em um artigo sua experiência em tornar a exceção do STL segura:

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

Veja o 7º ponto (Teste automatizado para segurança de exceção), onde ele se baseia em testes de unidade automatizados para garantir que todos os casos sejam testados. Eu acho que essa parte é uma excelente resposta para a pergunta do autor " Você pode ter certeza disso? ".

Edit 31-05-2013: Comentário do dionadar

t.integer += 1;é sem a garantia de que o excesso não acontecerá NÃO é uma exceção segura e, de fato, pode invocar tecnicamente o UB! (O estouro assinado é UB: C ++ 11 5/4 "Se durante a avaliação de uma expressão, o resultado não for matematicamente definido ou não estiver no intervalo de valores representáveis ​​para seu tipo, o comportamento será indefinido.") Observe que não está assinado. inteiro não transborda, mas faz seus cálculos em uma classe de equivalência módulo 2 ^ # bits.

Dionadar está se referindo à seguinte linha, que de fato tem um comportamento indefinido.

   t.integer += 1 ;                 // 1. nothrow/nofail

A solução aqui é verificar se o número inteiro já está no seu valor máximo (usando std::numeric_limits<T>::max()) antes de fazer a adição.

Meu erro iria na seção "Falha normal vs. bug", ou seja, um bug. Ele não invalida o raciocínio e não significa que o código de exceção-seguro seja inútil porque impossível de obter. Você não pode se proteger contra o desligamento do computador, ou erros do compilador, ou mesmo seus erros ou outros erros. Você não pode alcançar a perfeição, mas pode tentar chegar o mais próximo possível.

Corrigi o código com o comentário do Dionadar em mente.


6
Muito obrigado! Eu ainda esperava algo diferente. Mas aceito sua resposta por seguir o C ++ e por sua explicação detalhada. Você até refutou minha ofensa "Isso torna qualquer linha de código C ++ vulnerável a uma exceção". Afinal, essa análise linha por linha faz sentido ... Eu tenho que pensar sobre isso.
Frunsi

8
"Uma troca nunca falha". Esta é uma boa recomendação para funções swap () escritas sob encomenda. Deve-se notar, no entanto, que std :: swap () pode falhar com base nas operações que ele usa internamente.
Nobar

7
@ Mehrdad:: "finally" does exactly what you were trying to achieveClaro que sim. E como é fácil produzir código frágil finally, a noção de "tentar com o recurso" foi finalmente introduzida no Java 7 (ou seja, 10 anos após os C # usinge 3 décadas após os destruidores de C ++). Essa é a fragilidade que eu critico. Quanto a Just because [finally] doesn't match your taste (RAII doesn't match mine, [...]) doesn't mean it's "failing": nisso, a indústria discorda do seu gosto, pois as linguagens Garbage Collected tendem a adicionar instruções inspiradas em RAII (C # usinge Java try).
23412 paercebal

5
@ Mehrdad:: RAII doesn't match mine, since it needs a new struct every single darn time, which is tedious sometimesNão, não. Você pode usar ponteiros inteligentes ou classes de utilitário para "proteger" os recursos.
23412 paercebal

5
@ Mehrdad: "obriga a criar um invólucro para algo que talvez não seja necessário" - ao codificar da maneira RAII, você não precisará de nenhum tipo de invólucro: o recurso é o objeto e a vida útil dos objetos é a vida útil dos recursos. No entanto, eu vindo de um mundo C ++, atualmente luto com um projeto Java. Comparado com o RAII em C ++, esse "gerenciamento manual de recursos" em Java É MUITO MAL! Minha opinião, mas há muito tempo o Java negociava "mgmt de memória automática" por "mgmt de recurso automático".
Frunsi

32

Escrever código com exceção de segurança no C ++ não se resume a usar muitos blocos try {} catch {}. Trata-se de documentar que tipo de garantias fornece seu código.

Eu recomendo a leitura da série Guru da Semana de Herb Sutter , em especial as seções 59, 60 e 61.

Para resumir, existem três níveis de segurança de exceção que você pode fornecer:

  • Básico: quando seu código lança uma exceção, ele não vaza recursos e os objetos permanecem destrutíveis.
  • Forte: quando seu código lança uma exceção, ele mantém o estado do aplicativo inalterado.
  • Sem lance: seu código nunca gera exceções.

Pessoalmente, descobri esses artigos bastante tarde, muito do meu código C ++ definitivamente não é seguro para exceções.


1
Seu livro "Excepcional C ++" também é uma boa leitura. Mas ainda tento questionar o conceito de EH ...
Frunsi 05/12/2009

8
O +1 OP combina o tratamento de exceções (captura-as) com a segurança de exceção (geralmente mais sobre RAII)
jk.

Eu suspeito que muito pouco código C ++ de produção seja seguro para exceções.
Raedwald 31/03

18

Alguns de nós usam exceção há mais de 20 anos. A PL / I os possui, por exemplo. A premissa de que são uma tecnologia nova e perigosa me parece questionável.


Por favor, não me interpretem mal, estou (ou estou tentando) questionar EH. E especialmente C ++ EH. E eu estou procurando por alternativas. Talvez eu tenha que aceitá-lo (e aceito se for o único caminho), mas acho que poderia haver alternativas melhores. Não é que eu acho que o conceito é novo, mas sim, eu acho que pode ser mais perigoso do que o tratamento de erros explícita com códigos de retorno ...
Frunsi

1
Se você não gostar, não use. Coloque blocos de tentativa em torno das coisas que você precisa chamar, que podem lançar, e reinventar o código de erro do tipo antigo, e sofra com os problemas que ele tem, que em alguns casos são toleráveis.
bmargulies

Ok, perfeito, vou usar EH e códigos de erro e viver com isso. Eu sou um idiota, eu deveria ter chegado a essa solução sozinho! ;)
Frunsi

17

Primeiro de tudo (como Neil afirmou), o SEH é o tratamento de exceções estruturadas da Microsoft. É semelhante, mas não idêntico, ao processamento de exceção em C ++. De fato, você precisa habilitar o Manipulação de Exceção C ++, se quiser no Visual Studio - o comportamento padrão não garante que os objetos locais sejam destruídos em todos os casos! Em ambos os casos, o tratamento de exceções não é realmente mais difícil , é apenas diferente .

Agora, para suas perguntas reais.

Você realmente escreve código de exceção seguro?

Sim. Esforço-me pelo código seguro de exceção em todos os casos. Eu evangelizo usando técnicas RAII para escopo de acesso a recursos (por exemplo, boost::shared_ptrpara memória, boost::lock_guardpara bloqueio). Em geral, o uso consistente de RAII e técnicas de proteção de escopo facilitarão muito a gravação de códigos seguros de exceção. O truque é aprender o que existe e como aplicá-lo.

Tem certeza de que seu último código "pronto para produção" é seguro contra exceções?

Não. É tão seguro quanto é. Posso dizer que não vi uma falha no processo devido a uma exceção em vários anos de atividade 24/7. Não espero um código perfeito, apenas um código bem escrito. Além de fornecer segurança de exceção, as técnicas acima garantem a correção de uma maneira quase impossível de alcançar com try/ catchblocks. Se você estiver capturando tudo em seu escopo de controle superior (encadeamento, processo etc.), pode ter certeza de que continuará executando em face de exceções (na maioria das vezes ). As mesmas técnicas também ajudarão você a continuar a executar corretamente em face de exceções sem try/ catchbloqueia em todos os lugares .

Você pode ter certeza disso?

Sim. Você pode ter certeza de uma auditoria de código completa, mas ninguém realmente faz isso? No entanto, revisões regulares de código e desenvolvedores cuidadosos ajudam bastante a chegar lá.

Você conhece e / ou realmente usa alternativas que funcionam?

Eu tentei algumas variações ao longo dos anos, como estados de codificação nos bits superiores (ala HRESULTs ) ou naquele setjmp() ... longjmp()corte horrível . Ambos se decompõem na prática, embora de maneiras completamente diferentes.


No final, se você adquirir o hábito de aplicar algumas técnicas e pensar cuidadosamente sobre onde você pode realmente fazer algo em resposta a uma exceção, você terá um código muito legível que é seguro para exceções. Você pode resumir isso seguindo estas regras:

  • Você só quer ver try/ catchquando pode fazer algo sobre uma exceção específica
  • Você quase nunca deseja ver um código bruto newou deleteem código
  • Evitar std::sprintf, snprintfe matrizes em geral - use std::ostringstreampara formatar e substituir matrizes por std::vectorestd::string
  • Em caso de dúvida, procure a funcionalidade no Boost ou STL antes de lançar seu próprio

Só posso recomendar que você aprenda a usar as exceções corretamente e esqueça os códigos de resultado se planeja escrever em C ++. Se você deseja evitar exceções, considere escrever em outro idioma que não as possua ou as proteja . Se você realmente quiser aprender a utilizar totalmente o C ++, leia alguns livros de Herb Sutter , Nicolai Josuttis e Scott Meyers .


"o comportamento padrão não garante que objetos locais sejam destruídos em todos os casos" Você está dizendo que a configuração padrão do compilador do Visual Studio C ++ produz código incorreto diante de exceções. É mesmo assim?
Raedwald 31/03

"Você quase nunca quer ver um crua newou deleteem código": por matéria- Eu acho que você quer dizer fora de um construtor ou destruidor.
Raedwald 31/03

@Raedwald - re: VC ++: a edição VS2005 do VC ++ não destrói objetos locais quando uma exceção SEH é lançada. Leia o "ativar o tratamento de exceção do C ++" . No VS2005, as exceções SEH não chamam os destruidores de objetos C ++ por padrão. Se você chamar funções Win32 ou qualquer coisa definida em uma DLL da interface C, precisará se preocupar com isso, pois elas podem (e ocasionalmente) lançam uma exceção SEH do seu jeito.
precisa saber é o seguinte

@ Raedwald: re: raw : basicamente, deletenunca deve ser usado fora da implementação tr1::shared_ptre similares. newpode ser usado desde que seu uso seja algo parecido tr1::shared_ptr<X> ptr(new X(arg, arg));. A parte importante é que o resultado newvai diretamente para um ponteiro gerenciado. A página de boost::shared_ptrpráticas recomendadas descreve as melhores.
D.Shawley

Por que você se refere a std :: sprintf (et al) em seu conjunto de regras para segurança de exceção? Estes não documentar que eles jogam quaisquer excepções, por exemplo en.cppreference.com/w/cpp/io/c/fprintf
mabraham

10

Não é possível escrever código com exceção de segurança sob o pressuposto de que "qualquer linha pode lançar". O design do código com exceção de segurança depende criticamente de certos contratos / garantias que você deve esperar, observar, seguir e implementar em seu código. É absolutamente necessário ter um código que nunca será lançado. Existem outros tipos de garantias de exceção por aí.

Em outras palavras, criar código com exceção de segurança é, em grande parte, uma questão de design de programa , não apenas uma questão de codificação simples .


8
  • Você realmente escreve código de exceção seguro?

Bem, certamente pretendo.

  • Tem certeza de que seu último código "pronto para produção" é seguro contra exceções?

Tenho certeza de que meus servidores 24/7, construídos usando exceções, funcionam 24 horas por dia, 7 dias por semana e não perdem memória.

  • Você pode ter certeza disso?

É muito difícil ter certeza de que qualquer código está correto. Normalmente, só se pode obter resultados

  • Você conhece e / ou realmente usa alternativas que funcionam?

Não. Usar exceções é mais limpo e fácil do que qualquer uma das alternativas que usei nos últimos 30 anos em programação.


30
Esta resposta não tem valor.
Matt Joiner

6
@MattJoiner Então a pergunta não tem valor.
Roteamento de milhas

5

Deixando de lado a confusão entre as exceções SEH e C ++, você precisa estar ciente de que as exceções podem ser lançadas a qualquer momento e escrever seu código com isso em mente. A necessidade de segurança de exceção é o que impulsiona o uso de RAII, ponteiros inteligentes e outras técnicas modernas de C ++.

Se você seguir os padrões bem estabelecidos, escrever código com exceção de segurança não é particularmente difícil e, na verdade, é mais fácil do que escrever código que lida com retornos de erro corretamente em todos os casos.


3

EH é bom, geralmente. Mas a implementação do C ++ não é muito amigável, pois é realmente difícil dizer quão boa é a cobertura de exceção. Java, por exemplo, facilita isso, o compilador tenderá a falhar se você não lidar com possíveis exceções.


2
É muito melhor com o uso criterioso de noexcept.
Einpoklum

2

Eu realmente gosto de trabalhar com Eclipse e Java (novo no Java), porque gera erros no editor se você estiver com falta de um manipulador EH. Isso torna muito mais difícil esquecer as coisas para lidar com uma exceção ...

Além disso, com as ferramentas IDE, ele adiciona o bloco try / catch ou outro bloco catch automaticamente.


5
Essa é a diferença entre as exceções verificadas (Java) e não verificadas (c ++) (Java também possui algumas). A vantagem das exceções verificadas é o que você escreveu, mas há suas próprias desvantagens. Google para as abordagens de diferença e os diferentes problemas que eles têm.
David Rodríguez - dribeas 5/12/2009

2

Alguns de nós preferem linguagens como Java, que nos obrigam a declarar todas as exceções geradas pelos métodos, em vez de torná-las invisíveis, como em C ++ e C #.

Quando feitas corretamente, as exceções são superiores aos códigos de retorno de erro, se por nenhum outro motivo, você não precisar propagar falhas manualmente na cadeia de chamadas.

Dito isto, a programação da biblioteca de API de baixo nível provavelmente deve evitar o tratamento de exceções e seguir os códigos de retorno de erro.

Tem sido minha experiência que é difícil escrever um código de manipulação de exceção limpo em C ++. Eu acabo usando new(nothrow)muito.


4
E você está evitando a maior parte da biblioteca padrão também? Usar new(std::nothrow)não é suficiente. By the way, é mais fácil de código de exceção-safe escrita em C ++ do que em Java: en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization
avakar

3
A usabilidade da exceção verificada em Java é altamente exagerada. De fato, linguagens não Java, elas NÃO são consideradas um sucesso. É por isso que a instrução "throw" no C ++ agora é considerada obsoleta e o C # nunca considerou seriamente implementá-las (essa foi uma opção de design). Para Java, o seguinte documento pode ser esclarecedor
paercebal

2
Minha experiência é que escrever código com exceção de segurança em C ++ não é tão difícil e tende a levar a códigos mais limpos em geral. Claro, você precisa aprender a fazê-lo.
David Thornley

2

Eu tento o meu melhor para escrever código com exceção de segurança, sim.

Isso significa que eu tenho o cuidado de observar quais linhas podem ser lançadas. Nem todo mundo pode, e é extremamente importante manter isso em mente. A chave é realmente pensar e projetar seu código para satisfazer as garantias de exceção definidas no padrão.

Esta operação pode ser gravada para fornecer uma forte garantia de exceção? Eu tenho que me contentar com o básico? Quais linhas podem gerar exceções e como posso garantir que, se o fizerem, não corromperão o objeto?


2
  • Você realmente escreve código de exceção seguro? [Nao existe tal coisa. Exceções são um escudo de papel para erros, a menos que você tenha um ambiente gerenciado. Isso se aplica às três primeiras perguntas.]

  • Você conhece e / ou realmente usa alternativas que funcionam? [Alternativa para o que? O problema aqui é que as pessoas não separam os erros reais da operação normal do programa. Se é uma operação normal do programa (ou seja, um arquivo não encontrado), não é realmente o tratamento de erros. Se for um erro real, não há como manipulá-lo ou não é um erro real. Seu objetivo aqui é descobrir o que deu errado e parar a planilha e registrar um erro, reiniciar o driver na sua torradeira ou apenas rezar para que o caça a jato possa continuar voando mesmo quando o software está com erros e espero o melhor.]


0

Muita gente (eu diria mesmo).

O que é realmente importante sobre as exceções é que, se você não escrever nenhum código de manipulação - o resultado será perfeitamente seguro e bem-comportado. Ansioso demais para entrar em pânico, mas seguro.

Você precisa cometer erros ativamente nos manipuladores para obter algo inseguro, e apenas catch (...) {} se compara a ignorar o código de erro.


4
Não é verdade. É muito fácil escrever código que não seja seguro para exceções. Por exemplo: f = new foo(); f->doSomething(); delete f;Se o método doSomething lançar uma exceção, haverá um vazamento de memória.
Kristopher Johnson

1
Não importa quando o programa termina, certo? Para continuar a execução, você precisará engolir exceções ativamente . Bem, existem alguns casos específicos em que a terminação sem limpeza ainda é inaceitável, mas essas situações precisam de cuidados especiais em qualquer linguagem e estilo de programação.
Ima

Você simplesmente não pode ignorar exceções (e não escrever código de manipulação), nem no C ++ nem no código gerenciado. Será inseguro e não se comportará bem. Exceto por talvez algum código de brinquedo.
Frunsi 6/12/09

1
Se você ignorar exceções no código do aplicativo, o programa ainda poderá não se comportar bem quando recursos externos estiverem envolvidos. É verdade que o sistema operacional se preocupa em fechar identificadores de arquivo, bloqueios, soquetes e assim por diante. Mas nem tudo é tratado, por exemplo, pode deixar arquivos desnecessários ou danificar arquivos enquanto você escreve para eles e assim por diante. Se você ignora as exceções, você tem um problema em Java, em C ++ você pode trabalhar com RAII (mas quando você usa a técnica RAII, provavelmente as usa porque se preocupa com exceções) ...
Frunsi

3
Por favor, não torça minhas palavras. Eu escrevi "se você não escrever nenhum código de manipulação" - e eu quis dizer exatamente isso. Para ignorar exceções, você precisa escrever um código.
Ima
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.