web-dev-qa-db-fra.com

Comment lancer une exception C ++

J'ai une très mauvaise compréhension de la gestion des exceptions (comment personnaliser les instructions throw, try, catch pour mes propres besoins).

Par exemple, j'ai défini une fonction comme suit: int compare(int a, int b){...}

Je voudrais que la fonction jette une exception avec un message quand a ou b est négatif.

Comment dois-je aborder ceci dans la définition de la fonction?

208
Terry Li

Facile:

#include <stdexcept>

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

La bibliothèque standard est livrée avec une belle collection de objets d'exception intégrés que vous pouvez lancer. Gardez à l'esprit que vous devriez toujours jeter par valeur et attraper par référence:

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

Vous pouvez avoir plusieurs instructions catch () après chaque essai. Vous pouvez donc gérer différents types d'exceptions séparément si vous le souhaitez.

Vous pouvez également relancer des exceptions:

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

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

Et pour intercepter des exceptions quel que soit leur type:

catch( ... ) { };
302
nsanders

Ajoutez simplement throw là où cela est nécessaire, et try bloquer à l'appelant qui traite l'erreur. Par convention, vous ne devez lancer que les éléments dérivés de std::exception, donc incluez <stdexcept> en premier.

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

En outre, regardez dans Boost.Exception .

16
Cat Plus Plus

Bien que cette question soit plutôt ancienne et que l'on y ait déjà répondu, je souhaite simplement ajouter une note sur la manière de gérer correctement les exceptions en C++ 11:

Utilisez std::nested_exception et std::throw_with_nested

Il est décrit sur StackOverflow ici et ici , comment vous pouvez obtenir une trace de vos exceptions dans votre code sans avoir besoin d’un débogueur ou d’une journalisation lourde, en écrivant simplement un gestionnaire d’exceptions approprié qui va redistribuer les exceptions imbriquées.

Comme vous pouvez le faire avec n'importe quelle classe d'exception dérivée, vous pouvez ajouter beaucoup d'informations à un tel backtrace! Vous pouvez également jeter un oeil à mon MWE sur GitHub , où une trace aurait l'apparence suivante:

_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"
_
12
GPMueller

Vous pouvez définir un message à envoyer lorsqu'une certaine erreur se produit:

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

ou vous pouvez le définir comme ceci:

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

Typiquement, vous auriez un bloc try ... catch comme ceci:

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

Je voulais AJOUTER aux autres réponses décrites ici une note supplémentaire, dans le cas de exceptions personnalisées .

Dans le cas où vous créez votre propre exception personnalisée, qui dérive de std::exception, lorsque vous identifiez "tous les types d'exceptions possibles", vous devez toujours démarrer les clauses catch avec le type d'exception "le plus dérivé" qui peut être attrapé. Voir l'exemple (de ce que PAS à faire):

#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;
}

NOTE:

0) Le bon ordre doit être inversement, c'est-à-dire d'abord catch (const MyException& e) suivi de catch (const std::exception& e).

1) Comme vous pouvez le constater, lorsque vous exécutez le programme en l’état, la première clause catch sera exécutée (ce qui est probablement ce que vous avez fait PAS voulu au départ) .

2) Même si le type intercepté dans la première clause catch est de type std::exception, la version "correcte" de what() sera appelée - car elle est interceptée par référence (modifie au moins l'argument intercepté std::exception saisissez une valeur - et vous ferez l'expérience du phénomène de "découpage d'objet" en action).

3) Dans le cas où "un code dû au fait que l'exception XXX a été levée ..." fait des choses importantes RELATIVEMENT au type d'exception, votre code est incorrect.

4) Ceci est également pertinent si les objets capturés étaient des objets "normaux" tels que: class Base{}; et class Derived : public Base {}...

5) g++ 7.3.0 sur Ubuntu 18.04.1 génère un avertissement indiquant le problème mentionné:

Dans la fonction 'void illustrDerivedExceptionCatch ()': item12Linux.cpp: 48: 2: warning: exception de type 'MyException' sera attrapé attrape (const MyException & e) ^ ~~~~

item12Linux.cpp: 43: 2: avertissement: par le gestionnaire précédent pour 'std :: exception' catch (const exception & e) ^ ~~~~

Encore une fois , je dirai que cette réponse ne concerne que AJOUTE aux autres réponses décrites ici (je pensais à ce point Il convient de mentionner, mais ne pouvait pas le décrire dans un commentaire).

4
Guy Avraham