Come generare un'eccezione C ++


260

Ho una comprensione molto scarsa della gestione delle eccezioni (ad esempio, come personalizzare lanciare, provare, catturare dichiarazioni per i miei scopi).

Ad esempio, ho definito una funzione come segue: int compare(int a, int b){...}

Vorrei che la funzione generasse un'eccezione con qualche messaggio quando a o b è negativo.

Come dovrei affrontarlo nella definizione della funzione?



37
@OliCharlesworth, non pensi che sia un po 'troppo da dare a qualcuno che è confuso dalle basi?
Mark Ransom,

6
Vale la pena evitare eccezioni superflue. Se non si desidera che il chiamante passi valori negativi, renderlo più ovvio specificando unsigned intcome parametri nella firma della funzione. Poi di nuovo sono della scuola che dovresti solo lanciare e catturare eccezioni per cose che sono davvero eccezionali.
AJG85,

1
@Mark: inizialmente avevo frainteso la domanda se si dovessero usare le throw()eccezioni sulle funzioni.
Oliver Charlesworth,

Risposte:


364

Semplice:

#include <stdexcept>

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

La libreria standard viene fornita con una bella raccolta di oggetti di eccezione incorporati che puoi lanciare. Tieni presente che devi sempre lanciare per valore e prendere per riferimento:

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

Puoi avere più istruzioni catch () dopo ogni tentativo, quindi puoi gestire diversi tipi di eccezione separatamente, se lo desideri.

Puoi anche rilanciare le eccezioni:

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

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

E per catturare le eccezioni indipendentemente dal tipo:

catch( ... ) { };

26
E dovresti sempre cogliere le eccezioni come const
Adrian Cornish,

2
@TerryLiYifeng se le eccezioni personalizzate hanno più senso, allora provaci. Potresti comunque voler derivare da std :: exception e mantenere l'interfaccia uguale.
nsanders,

2
+ Ancora una volta, ma penso che sia piuttosto importante - perché evidenzia il fatto che ora è un oggetto temporaneo - quindi la modifica è inutile.
Adrian Cornish,

2
@AdrianCornish: Non è però temporaneo. Le catture non costanti possono essere utili .
GManNickG,

26
Di solito ricominciare da capo con un semplice throw;(ridisegnare l'oggetto originale e preservarne il tipo) piuttosto che throw e;(lanciare una copia dell'oggetto catturato, eventualmente cambiandone il tipo).
Mike Seymour,

17

Basta aggiungere throwdove necessario e trybloccare il chiamante che gestisce l'errore. Per convenzione dovresti lanciare solo cose che derivano std::exception, quindi includi <stdexcept>prima.

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) {
        // ...
    }
}

Inoltre, esamina Boost.Exception .


15

Sebbene questa domanda sia piuttosto vecchia e abbia già ricevuto una risposta, voglio solo aggiungere una nota su come eseguire una corretta gestione delle eccezioni in C ++ 11:

Usa std::nested_exceptionestd::throw_with_nested

È descritto su StackOverflow qui e qui , come è possibile ottenere un backtrace sulle eccezioni all'interno del codice senza la necessità di un debugger o di una registrazione ingombrante, semplicemente scrivendo un gestore di eccezioni appropriato che riproporrà le eccezioni nidificate.

Dato che puoi farlo con qualsiasi classe di eccezione derivata, puoi aggiungere molte informazioni a tale backtrace! Puoi anche dare un'occhiata al mio MWE su GitHub , dove un backtrace sarebbe simile a questo:

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"

8

È possibile definire un messaggio da inviare quando si verifica un determinato errore:

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

o potresti definirlo così:

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

In genere, si avrebbe un try ... catchblocco come questo:

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

6

Voluto ADD alle altre risposte qui descritte una nota aggiuntiva, in caso di eccezioni personalizzate .

Nel caso in cui si crei la propria eccezione personalizzata, da cui deriva std::exception, quando si rilevano i tipi di eccezioni "tutti i possibili", è necessario avviare sempre le catchclausole con il tipo di eccezione "più derivato" che può essere rilevato. Vedi l'esempio (di cosa NON fare):

#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) L'ordine corretto dovrebbe essere viceversa, cioè prima tu catch (const MyException& e)che è seguito da catch (const std::exception& e).

1) Come puoi vedere, quando esegui il programma così com'è, verrà eseguita la prima clausola catch (che è probabilmente quello che NON volevi in ​​primo luogo).

2) Anche se il tipo catturato nella prima clausola catch è di tipo std::exception, what()verrà chiamata la versione "corretta" di - perché viene catturata per riferimento (cambia almeno il std::exceptiontipo di argomento catturato in base al valore - e sperimenterai il fenomeni di "slicing object" in azione).

3) Nel caso in cui "un po 'di codice a causa del fatto che l'eccezione XXX è stata generata ..." fa cose importanti PER QUANTO RIGUARDA il tipo di eccezione, qui c'è un comportamento scorretto del tuo codice.

4) Ciò è rilevante anche se gli oggetti catturati erano oggetti "normali" come: class Base{};e class Derived : public Base {}...

5) g++ 7.3.0su Ubuntu 18.04.1 produce un avviso che indica il problema menzionato:

Nella funzione 'void illustrateDerivedExceptionCatch ()': item12Linux.cpp: 48: 2: avviso: verrà catturata l' eccezione del tipo 'MyException' (const MyException & e) ^ ~~~~

item12Linux.cpp: 43: 2: avviso: dal gestore precedente per il catch 'std :: exception' (const exception & e) ^ ~~~~

Di nuovo , dirò, che questa risposta è solo da AGGIUNGERE alle altre risposte qui descritte (ho pensato che questo punto meriti di essere menzionato, ma non sono riuscito a descriverlo in un commento).

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.