Como lançar uma exceção C ++

260

Eu tenho um entendimento muito ruim do tratamento de exceções (ou seja, como personalizar instruções de throw, try, catch para meus próprios propósitos).

Por exemplo, eu defini uma função da seguinte maneira: int compare(int a, int b){...}

Eu gostaria que a função gerasse uma exceção com alguma mensagem quando a ou b for negativo.

Como devo abordar isso na definição da função?

Terry Li
fonte
3
Você deve ler o seguinte: gotw.ca/publications/mill22.htm .
Oliver Charlesworth 12/12
37
@OliCharlesworth, você não acha que isso é um pouco demais para alguém que está confuso com o básico?
Mark Ransom
6
Vale a pena evitar exceções supérfluas. Se você não deseja que seu chamador passe valores negativos, torne-o mais óbvio, especificando unsigned intcomo os parâmetros em sua assinatura de função. Por outro lado, sou da escola que você só deve lançar e capturar exceções para coisas que são realmente excepcionais.
AJG85
1
@ Mark: Originalmente, eu não entendi a pergunta sobre se alguém deveria usar throw()especificações de exceção em funções.
Oliver Charlesworth 12/12

Respostas:

364

Simples:

#include <stdexcept>

int compare( int a, int b ) {
    if ( a < 0 || b < 0 ) {
        throw std::invalid_argument( "received negative value" );
    }
}

A Biblioteca padrão vem com uma bela coleção de objetos de exceção internos que você pode lançar. Lembre-se de que você deve sempre jogar por valor e pegar por referência:

try {
    compare( -1, 3 );
}
catch( const std::invalid_argument& e ) {
    // do stuff with exception... 
}

Você pode ter várias instruções catch () após cada tentativa, para poder manipular diferentes tipos de exceção separadamente, se desejar.

Você também pode relançar exceções:

catch( const std::invalid_argument& e ) {
    // do something

    // let someone higher up the call stack handle it if they want
    throw;
}

E para capturar exceções, independentemente do tipo:

catch( ... ) { };
nsanders
fonte
26
E você deve sempre capturar exceções como const
Adrian Cornish
2
@TerryLiYifeng se as exceções personalizadas fizerem mais sentido, então faça isso. Você ainda pode derivar de std :: exception e manter a interface igual.
Nsanders
2
Marcou com +1 novamente, mas acho que const é muito importante - porque destaca o fato de ser um objeto temporário agora -, portanto, a modificação é inútil.
Adrian Cornish
2
@AdrianCornish: Não é realmente temporário. Capturas não constantes podem ser úteis .
precisa saber é o seguinte
26
Em geral, você throw;repetia a reprodução com um simples (repetindo o objeto original e preservando seu tipo) em vez de throw e;(jogando uma cópia do objeto capturado, possivelmente alterando seu tipo).
Mike Seymour
17

Basta adicionar throwonde for necessário e trybloquear no chamador que lida com o erro. Por convenção, você só deve jogar coisas que derivam std::exception; portanto, inclua <stdexcept>primeiro.

int compare(int a, int b) {
    if (a < 0 || b < 0) {
        throw std::invalid_argument("a or b negative");
    }
}

void foo() {
    try {
        compare(-1, 0);
    } catch (const std::invalid_argument& e) {
        // ...
    }
}

Além disso, consulte o Boost.Exception .

Cat Plus Plus
fonte
15

Embora essa pergunta seja bastante antiga e já tenha sido respondida, só quero adicionar uma observação sobre como lidar com exceções no C ++ 11:

Use std::nested_exceptionestd::throw_with_nested

É descrito no StackOverflow aqui e aqui , como você pode obter um retorno das suas exceções dentro do seu código sem a necessidade de um depurador ou log complicado, simplesmente escrevendo um manipulador de exceções adequado que irá repetir exceções aninhadas.

Como 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 algo como isto:

Library API: Exception caught in function 'api_function'
Backtrace:
~/Git/mwe-cpp-exception/src/detail/Library.cpp:17 : library_function failed
~/Git/mwe-cpp-exception/src/detail/Library.cpp:13 : could not open file "nonexistent.txt"
GPMueller
fonte
8

Você pode definir uma mensagem a ser lançada quando ocorrer um determinado erro:

throw std::invalid_argument( "received negative value" );

ou você pode defini-lo assim:

std::runtime_error greatScott("Great Scott!");          
double getEnergySync(int year) {                        
    if (year == 1955 || year == 1885) throw greatScott; 
    return 1.21e9;                                      
}                                                       

Normalmente, você teria um try ... catchbloco como este:

try {
// do something that causes an exception
}catch (std::exception& e){ std::cerr << "exception: " << e.what() << std::endl; }
serup
fonte
6

Queria ADD para as outras respostas aqui descritas uma nota adicional, no caso de exceções personalizadas .

No caso em que você cria sua própria exceção personalizada, que deriva de std::exception, quando você captura os tipos de exceções "todos os possíveis", sempre deve iniciar as catchcláusulas com o tipo de exceção "mais derivado" que pode ser capturado. Veja o exemplo (do que NÃO fazer):

#include <iostream>
#include <string>

using namespace std;

class MyException : public exception
{
public:
    MyException(const string& msg) : m_msg(msg)
    {
        cout << "MyException::MyException - set m_msg to:" << m_msg << endl;
    }

   ~MyException()
   {
        cout << "MyException::~MyException" << endl;
   }

   virtual const char* what() const throw () 
   {
        cout << "MyException - what" << endl;
        return m_msg.c_str();
   }

   const string m_msg;
};

void throwDerivedException()
{
    cout << "throwDerivedException - thrown a derived exception" << endl;
    string execptionMessage("MyException thrown");
    throw (MyException(execptionMessage));
}

void illustrateDerivedExceptionCatch()
{
    cout << "illustrateDerivedExceptionsCatch - start" << endl;
    try 
    {
        throwDerivedException();
    }
    catch (const exception& e)
    {
        cout << "illustrateDerivedExceptionsCatch - caught an std::exception, e.what:" << e.what() << endl;
        // some additional code due to the fact that std::exception was thrown...
    }
    catch(const MyException& e)
    {
        cout << "illustrateDerivedExceptionsCatch - caught an MyException, e.what::" << e.what() << endl;
        // some additional code due to the fact that MyException was thrown...
    }

    cout << "illustrateDerivedExceptionsCatch - end" << endl;
}

int main(int argc, char** argv)
{
    cout << "main - start" << endl;
    illustrateDerivedExceptionCatch();
    cout << "main - end" << endl;
    return 0;
}

NOTA:

0) A ordem correta deve ser vice-versa, ou seja, primeiro você catch (const MyException& e)é seguido por catch (const std::exception& e).

1) Como você pode ver, quando você executa o programa como está, a primeira cláusula catch será executada (o que provavelmente é o que você NÃO queria em primeiro lugar).

2) Embora o tipo capturado na primeira cláusula catch seja do tipo std::exception, a versão "adequada" what()será chamada - porque é capturada por referência (altere pelo menos o std::exceptiontipo de argumento capturado por valor - e você experimentará o fenômenos de "corte de objetos" em ação).

3) Caso o "algum código devido ao fato de a exceção XXX ter sido lançada ..." faça coisas importantes COM RESPEITO ao tipo de exceção, há um mau comportamento do seu código aqui.

4) Isso também é relevante se os objetos capturados forem objetos "normais" como: class Base{};e class Derived : public Base {}...

5) g++ 7.3.0no Ubuntu 18.04.1 produz um aviso que indica o problema mencionado:

Na função 'void illustrateDerivedExceptionCatch ()': item12Linux.cpp: 48: 2: warning: a exceção do tipo 'MyException' será capturada catch (const MyException & e) ^ ~~~~

item12Linux.cpp: 43: 2: warning: pelo manipulador anterior para a captura 'std :: exception' (exceção const & e) ^ ~~~~

Mais uma vez , eu vou dizer, que esta resposta é apenas para ADD para as outras respostas aqui descritas (Eu pensei que este ponto vale a pena mencionar, ainda não poderia representá-lo dentro de um comentário).

Guy Avraham
fonte