Eu vi pelo menos uma fonte confiável (uma classe C ++ que eu fiz) recomendar que as classes de exceção específicas do aplicativo em C ++ devem herdar de std::exception
. Não estou certo sobre os benefícios dessa abordagem.
Em C #, as razões para herdar de ApplicationException
são claras: você obtém um punhado de métodos, propriedades e construtores úteis e só precisa adicionar ou substituir o que precisa. Com std::exception
isso, parece que tudo o que você obtém é um what()
método para substituir, que você mesmo poderia criar.
Então, quais são os benefícios, se houver, de usar std::exception
como classe base para minha classe de exceção específica do aplicativo? Existem boas razões para não herdar std::exception
?
c++
exception
exception-handling
John M Gant
fonte
fonte
Respostas:
O principal benefício é que o código que usa suas classes não precisa saber o tipo exato do que você está
throw
fazendo, mas pode apenascatch
ostd::exception
.Edit: como Martin e outros observaram, você realmente deseja derivar de uma das subclasses de
std::exception
declarado no<stdexcept>
cabeçalho.fonte
std::exception
é definido no<exception>
cabeçalho ( prova ).-1
O problema
std::exception
é que não existe um construtor (nas versões compatíveis com o padrão) que aceita uma mensagem.Como resultado, prefiro derivar de
std::runtime_error
. Isso é derivado de,std::exception
mas seus construtores permitem que você passe uma C-String ou umstd::string
para o construtor que será retornado (como achar const*
) quandowhat()
for chamado.fonte
A razão para herdar de
std::exception
é uma classe base "padrão" para exceções, portanto, é natural que outras pessoas em uma equipe, por exemplo, esperem isso e capturem a basestd::exception
.Se você estiver procurando por conveniência, pode herdar
std::runtime_error
daquelestd::string
construtor de provedores .fonte
std::exception
é uma ideia excelente . O que você não deve fazer é herdar de mais de um.Certa vez, participei da limpeza de uma grande base de código onde os autores anteriores haviam lançado ints, HRESULTS, std :: string, char *, classes aleatórias ... coisas diferentes em todos os lugares; basta citar um tipo e provavelmente foi jogado em algum lugar. E nenhuma classe base comum. Acredite em mim, as coisas ficaram muito mais organizadas quando chegamos ao ponto em que todos os tipos lançados tinham uma base comum que poderíamos pegar e saber que nada iria passar. Então, por favor, faça a si mesmo (e àqueles que terão que manter seu código no futuro) um favor e faça assim desde o início.
fonte
Você deve herdar de boost :: exception . Ele fornece muito mais recursos e maneiras bem conhecidas de transportar dados adicionais ... claro, se você não estiver usando o Boost , ignore esta sugestão.
fonte
Sim, você deve derivar
std::exception
.Outros responderam que
std::exception
tem o problema de você não conseguir passar uma mensagem de texto para ele; no entanto, geralmente não é uma boa ideia tentar formatar uma mensagem do usuário no momento do lançamento. Em vez disso, use o objeto de exceção para transportar todas as informações relevantes para o site de captura, que pode então formatar uma mensagem amigável.fonte
std::exception
e transportar as informações da exceção. Mas quem será o responsável por construir / formatar a mensagem de erro? A::what()
função ou outra coisa?O motivo pelo qual você pode querer herdar de
std::exception
é porque ele permite que você lance uma exceção que é capturada de acordo com essa classe, ou seja:fonte
Há um problema com herança que você deve conhecer é o fatiamento de objetos. Quando você escreve
throw e;
uma expressão de lançamento, inicializa um objeto temporário, chamado de objeto de exceção, o tipo do qual é determinado removendo quaisquer qualificadores cv de nível superior do tipo estático do operando dethrow
. Isso pode não ser o que você está esperando. Exemplo de problema que você pode encontrar aqui .Não é um argumento contra a herança, é apenas uma informação 'obrigatória'.
fonte
throw;
está tudo bem, mas não é óbvio que você deva escrever algo assim.raise()
como tal:virtual void raise() { throw *this; }
. Apenas lembre-se de substituí-lo em cada exceção derivada.Diferença: std :: runtime_error vs std :: exception ()
Se você deve herdar ou não, depende de você. Padrão
std::exception
e seus descendentes de padrões propõem uma possível estrutura de hierarquia de exceção (divisão emlogic_error
sub - hierarquia eruntime_error
sub - hierarquia) e uma possível interface de objeto de exceção. Se você gosta - use. Se por algum motivo você precisar de algo diferente - defina sua própria estrutura de exceção.fonte
Como a linguagem já lança std :: exception, você precisa capturá-la de qualquer maneira para fornecer um relatório de erro decente. Você também pode usar essa mesma captura para todas as suas próprias exceções inesperadas. Além disso, quase qualquer biblioteca que lança exceções as derivaria de std :: exception.
Em outras palavras, é
ou
E a segunda opção é definitivamente melhor.
fonte
Se todas as suas possíveis exceções derivarem de
std::exception
, seu bloco catch pode simplesmentecatch(std::exception & e)
e ter a certeza de capturar tudo.Depois de capturar a exceção, você pode usar esse
what
método para obter mais informações. C ++ não oferece suporte à digitação de pato, portanto, outra classe com umwhat
método exigiria uma captura diferente e um código diferente para usá-lo.fonte
Derivar de qualquer tipo de exceção padrão ou não é a primeira questão. Isso habilita um único manipulador de exceção para todas as exceções da biblioteca padrão e o seu próprio, mas também incentiva esses manipuladores pega-todos. O problema é que só devemos capturar exceções com as quais sabemos lidar. Em main (), por exemplo, capturar todas as exceções std :: é provavelmente uma coisa boa se a string what () for registrada como último recurso antes de sair. Em outro lugar, no entanto, é improvável que seja uma boa ideia.
Depois de decidir se deve derivar de um tipo de exceção padrão ou não, a questão é qual deve ser a base. Se seu aplicativo não precisa de i18n, você pode pensar que formatar uma mensagem no site da chamada é tão bom quanto salvar informações e gerar a mensagem no site da chamada. O problema é que a mensagem formatada pode não ser necessária. Melhor usar um esquema de geração de mensagem preguiçoso - talvez com memória pré-alocada. Então, se a mensagem for necessária, ela será gerada no acesso (e, possivelmente, armazenada em cache no objeto de exceção). Portanto, se a mensagem é gerada quando lançada, então uma derivada std :: exception, como std :: runtime_error é necessária como a classe base. Se a mensagem for gerada lentamente, std :: exception é a base apropriada.
fonte
Outro motivo para exceções de subclasse é um aspecto de design melhor ao trabalhar em grandes sistemas encapsulados. Você pode reutilizá-lo para coisas como mensagens de validação, consultas do usuário, erros fatais do controlador e assim por diante. Em vez de reescrever ou refazer todas as suas mensagens de validação, você pode simplesmente "pegá-las" no arquivo de origem principal, mas lançar o erro em qualquer lugar em todo o seu conjunto de classes.
por exemplo, uma exceção fatal encerrará o programa, um erro de validação apenas limpará a pilha e uma consulta do usuário fará uma pergunta ao usuário final.
Fazer dessa forma também significa que você pode reutilizar as mesmas classes, mas em interfaces diferentes. por exemplo, um aplicativo do Windows pode usar a caixa de mensagem, um serviço da web mostrará html e o sistema de relatórios irá registrá-lo e assim por diante.
fonte
Embora esta pergunta seja bastante antiga e já tenha sido bastante respondida, eu só quero adicionar uma nota sobre como fazer o tratamento adequado de exceções em C ++ 11, uma vez que estou continuamente perdendo isso nas discussões sobre exceções:
Use
std::nested_exception
estd::throw_with_nested
É descrito em StackOverflow aqui e aqui , como você pode obter um backtrace em suas exceções dentro de seu código sem a necessidade de um depurador ou registro complicado, simplesmente escrevendo um manipulador de exceção adequado que irá relançar exceções aninhadas.
Visto que você pode fazer isso com qualquer classe de exceção derivada, você pode adicionar muitas informações a esse backtrace! Você também pode dar uma olhada no meu MWE no GitHub , onde um backtrace seria mais ou menos assim:
Você nem mesmo precisa criar uma subclasse
std::runtime_error
para obter muitas informações quando uma exceção é lançada.O único benefício que vejo na criação de subclasses (em vez de apenas usar
std::runtime_error
) é que seu manipulador de exceções pode capturar sua exceção personalizada e fazer algo especial. Por exemplo:fonte