
FAQ C++Consultez toutes les FAQ
Nombre d'auteurs : 34, nombre de questions : 368, derni�re mise � jour : 14 novembre 2021 Ajouter une question
Cette FAQ a �t� r�alis�e � partir des questions fr�quemment pos�es sur les forums de http://www.developpez.com et de l'exp�rience personnelle des auteurs.
Je tiens � souligner que cette FAQ ne garantit en aucun cas que les informations qu'elle propose sont correctes ; les auteurs font le maximum, mais l'erreur est humaine. Cette FAQ ne pr�tend pas non plus �tre compl�te. Si vous trouvez une erreur ou si vous souhaitez devenir r�dacteur, lisez ceci.
Sur ce, nous vous souhaitons une bonne lecture.
- Qu'est-ce qu'une exception ?
- Comment lever une exception ?
- Comment capturer les exceptions dans mon code ?
- Pourquoi faut-il capturer les exceptions par r�f�rence ?
- Est-il possible de capturer plusieurs exceptions dans un seul catch ?
- Comment relancer une exception que l'on a captur� ?
- Que se passe-t-il si aucun bloc catch n'existe pour traiter une exception ?
- Comment cr�er son propre type d'exception ?
- Peut-on lever des exceptions dans les constructeurs ?
- Peut-on lever des exceptions dans les destructeurs ?
- [C++11] Comment indiquer qu'une fonction ne l�ve jamais d'exception ?
- [C++98] Comment indiquer qu'une fonction ne l�ve jamais d'exception ?
- Quel est l'�quivalent C++ du bloc finally des autres langages ?
Les exceptions sont un nouveau moyen de g�rer les erreurs dans les programmes. La grande diff�rence vis � vis du classique code d'erreur renvoy� par une fonction est qu'une exception se propage depuis l'appel� vers l'appelant jusqu'� ce qu'elle rencontre un bloc de code qui s'occupe de la traiter. Au contraire d'un code d'erreur qui peut �tre ignor� (ce qui est malheureusement souvent le cas) une exception doit �tre trait�e. Si elle ne l'est pas dans la fonction qui en est � l'origine, elle doit l'�tre dans l'une des fonctions appelantes. Le compilateur s'occupe tout seul de faire en sorte que l'exception remonte le long de la pile des appels jusqu'� l'endroit o� un bloc a �t� pr�vu pour la traiter. Cela permet donc de facilement faire � remonter � les erreurs des fonctions appel�es vers les fonctions appelantes. L'apparition d'une exception interrompt l'ex�cution normale du programme et provoque sa reprise dans le gestionnaire d'exception le plus proche (qui peut se trouver beaucoup plus en amont dans une fonction appelante).
Le programmeur n'a donc plus � se soucier de tester la r�ussite ou non des fonctions qu'il appelle au moyen d'un grand nombre de tests comme dans l'exemple suivant :
Code c++ : | S�lectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | // ces fonctions renvoient false en cas d'erreur bool F1(); bool F2(); bool F3(); bool F4(); bool Test1() { // appeler F1 et F2 if ( !F1() ) { return false; } if ( !F2() ) { return false; } return true; } bool Test2() { // appeler Test1 et F3 if ( !Test1() ) { return false; } if ( !F3() ) { return false; } return true; } bool Test3() { // appeler Test2 et F4 if ( !Test2() ) { return false; } if ( !F4() ) { return false; } return true; } int main() { if ( !Test3() ) { std::cerr << "Une erreur est survenue, mais je ne sais pas o� !"; } } |
Code c++ : | S�lectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | // ces fonctions l�vent des exceptions en cas d'erreur void F1(); void F2(); void F3(); void F4(); void Test1() { F1(); F2(); } void Test2() { Test1(); F3(); } void Test3() { Test2(); F4(); } int main() { try { Test3(); } catch ( const std::bad_alloc & ) { std::cerr << "Erreur : m�moire insuffisante.\n"; } catch ( const std::out_of_range & ) { std::cerr << "Erreur : d�bordement de m�moire.\n"; } } |
Les exceptions sont d�clench�es gr�ce � l'utilisation du mot-cl� throw :
Code c++ : | S�lectionner tout |
1 2 | // l�ve une exception de type e throw e; |
Code c++ : | S�lectionner tout |
1 2 3 4 5 6 7 8 9 | try { // instructions pouvant d�clencher des exceptions // d�rivant de std::exception } catch ( const std::exception & e ) { std::cerr << e.what(); } |
Code c++ : | S�lectionner tout |
1 2 3 4 5 6 7 8 9 | try { // d�clencher une exception par pointeur throw new int( 10 ); } catch ( const int * e ) { std::cerr << "Erreur num�ro " << *e; } |
Code c++ : | S�lectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | #include <iostream> #include <stdexcept> int main() { try { // std::logic_error est une classe standard // qui d�rive de std::exception throw std::logic_error( "Exemple d'exception" ); } catch ( const std::exception & e ) { // affiche "Exemple d'exception" std::cerr << e.what(); } } |
Code c++ : | S�lectionner tout |
1 2 3 4 5 6 7 8 | try { throw "Message d'erreur"; } catch ( const char * Msg ) { std::cerr << Msg; } |
Si vous persistez � vouloir utiliser de simple cha�nes de caract�res au lieu d'une classe d�rivant de std::exception, utilisez au moins le type cha�ne de caract�res du C++ : std::string.
Code c++ : | S�lectionner tout |
1 2 3 4 5 6 7 8 | try { throw std::string( "Message d'erreur" ); } catch ( const std::string & Msg ) { std::cerr << Msg; } |
Le code susceptible de d�clencher des exceptions doit �tre plac� dans un bloc try...catch (essaye...attrape) de cette mani�re :
Code c++ : | S�lectionner tout |
1 2 3 4 5 6 7 8 9 10 | int * ptr; try { // tenter d'allouer 100 entiers ptr = new int [ 100 ]; } catch ( const std::bad_alloc & ) { // �chec de l'allocation } |
Code c++ : | S�lectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | try { // cr�er un tableau de taille 10 std::vector<int> tableau( 10 ); // acc�der au 11� �l�ment tableau.at( 10 ); } catch ( const std::exception & Exp ) { std::cerr << "Erreur : " << Exp.what() << ".\n"; } catch ( const std::bad_alloc & ) { std::cerr << "Erreur : m�moire insuffisante.\n"; } catch ( const std::out_of_range & ) { std::cerr << "Erreur : d�bordement de m�moire.\n"; } |
Il existe aussi un moyen d'attraper toutes les exceptions, en utilisant une ellipse (...) comme type de l'exception. Mais alors il n'y a aucun moyen de conna�tre l'origine et le type de l'exception (sauf � la relancer et la traiter dans un nouveau bloc try...catch). L'utilisation de cette forme g�n�rique doit �tre restreinte car elle ne permet de savoir si l'exception captur�e peut �tre trait�e et ignor�e ou si elle n�cessite de terminer le programme (corruption de la m�moire, etc.). On l'utilise en g�n�ral comme dernier recours.
Code c++ : | S�lectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | try { // cr�er un tableau de taille 10 std::vector<int> tableau( 10 ); // acc�der au 11� �l�ment tableau.at( 10 ); } catch ( const std::bad_alloc & ) { std::cerr << "Erreur : m�moire insuffisante.\n"; } catch ( const std::out_of_range & ) { std::cerr << "Erreur : d�bordement de m�moire.\n"; } catch ( const std::exception & Exp ) { std::cerr << "Erreur : " << Exp.xhat() << ".\n"; } catch ( ... ) // traite toutes les autres exceptions { std::cerr << "Erreur inconnue.\n"; } |
Notez que les exceptions sont r�cup�r�es par r�f�rence, comme cela est expliqu� dans la question

Comme discut� dans la question Comment lever une exception ?, il est fortement recommand� de lever des exceptions par valeur. En revanche, il vaut mieux les capturer par r�f�rence et non pas par valeur. Tout d'abord cela permet d'�viter une recopie, mais aussi et surtout cela permet de conserver le polymorphisme. L'exemple suivant illustre les probl�mes pos�s par un traitement des exceptions par valeur�:
Code c++ : | S�lectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | #include <iostream> #include <stdexcept> int main() { using namespace std; try { // std::logic_error h�rite de std::exception throw logic_error( "exception de test" ); } catch ( exception e ) // traitement par valeur { cerr << e.what(); } } |
Code c++ : | S�lectionner tout |
catch ( const exception & e ) // traitement par r�f�rence
Donc � moins de rechercher volontairement ce comportement, il est recommand� de traiter les exceptions par r�f�rence, de pr�f�rence constantes afin de permettre au compilateur d'effectuer des optimisations.
Malheureusement, non, ce m�canisme n'est pas possible. Un catch ne pouvant capturer qu'un seul type d'exceptions, il faut d�finir autant de blocs try/catch qu'il y a d'exceptions possibles.
L'utilisation de
Code c++ : | S�lectionner tout |
1 2 3 4 5 | try { // ... } catch(...) { // ... } |
Le mot-cl� throw permet de lever une nouvelle exception, mais aussi de relancer celle qui est en cours de traitement.
Code c++ : | S�lectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | #include <iostream> #include <stdexcept> void Test() { try { throw std::logic_error( "Exception de test" ); } catch ( const std::logic_error & e ) { std::cerr << "L'exception '" << e.what() << "' a �t� lev�e et va �tre relanc�e.\n"; throw; // relancer l'exception courante } } int main() { try { Test(); } catch ( const std::logic_error & e ) { std::cerr << "Erreur : " << e.what() << ".\n"; } } |
Lorsqu'une exception est d�clench�e, le compilateur recherche un bloc catch capable de la traiter. S'il n'en trouve pas, il remonte la pile d'ex�cution (d�roulage de la pile) afin d'en trouver un plus en amont dans la hi�rarchie des appels. D�piler un appel revient � quitter une fonction. A cette occasion ses objets locaux sont d�truits et les destructeurs appel�s, ce qui permet de quitter proprement la fonction en lib�rant toutes les ressources acquises si les destructeurs on �t� bien �crits. L'objet qui a servi � lever l'exception est lui m�me d�truit car il est local � la fonction. C'est pourquoi l'objet qui est transmis aux blocs catch est toujours une copie de l'objet initial qui a d�clench� l'exception.
Si la pile des appels est vid�e (donc que l'on est arriv� � main) et qu'aucun bloc catch satisfaisant n'a �t� trouv�, la fonction standard terminate est appel�e ce qui provoque par d�faut un arr�t pur et simple du programme. Ce comportement peut �tre modifi� au moyen de la fonction set_terminate d�finie dans l'en-t�te standard <exception>. Cette fonction installe un nouveau gestionnaire et renvoie l'adresse du pr�c�dent. Elle s'utilise de cette mani�re :
Code c++ : | S�lectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | #include <iostream> #include <exception> // ancien gestionnaire void (*old_handler)(); // gestionnaire personnalis� void my_handler() { std::cerr << "Exception inattendue.\n"; // appel du gestionnaire par d�faut (old_handler)(); } int main() { // installer notre gestionnaire personnalis� old_handler = set_terminate( my_handler ); // lever une exception que l'on ne traite pas throw "test"; } |
This application has requested the Runtime to terminate it in an unusual way.
Please contact the application's support team for more information.
N'importe quel type de base ou classe C++ peut �tre utilis� comme type d'exception. Mais il est pr�f�rable de cr�er son type qui h�rite de la classe de base standard pour les exceptions : std::exception d�finie dans l'en-t�te <exception>. Cette classe poss�de une fonction membre virtuelle what() qu'il convient de red�finir :
Code c++ : | S�lectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | #include <iostream> #include <sstream> #include <exception> class my_exception : public std::exception { public: my_exception( const char * Msg, int Line ) { std::ostringstream oss; oss << "Erreur ligne " << Line << " : " << Msg; this->msg = oss.str(); } virtual ~my_exception() throw() { } virtual const char * what() const throw() { return this->msg.c_str(); } private: std::string msg; }; int main() { try { throw my_exception( "exception test", __LINE__ ); } catch ( const std::exception & e ) { std::cerr << e.what() << "\n"; } } |
Erreur ligne 29 : exception test
Tout � fait. C'est m�me une des seules mani�res d'indiquer que l'initialisation de l'objet a �chou�. Il faut cependant �tre prudent car une exception lev�e dans un constructeur peut �tre � l'origine de fuites de m�moires ou d'autres probl�mes de non lib�ration de ressources. C'est le cas dans l'exemple suivant :
Code c++ : | S�lectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | class Test { public: // exception bad_alloc en cas de m�moire insuffisante Test( int A, int B ) : tableau1( 0 ), tableau2( 0 ) { // ici tableau1 et tableau2 valent NULL, donc en cas d'�chec d'allocation // on peut appeler delete [] sans probl�me dans le destructeur this->tableau1 = new int[ A ]; this->tableau2 = new int[ B ]; } ~Test() { delete [] this->tableau2; delete [] this->tableau1; } private: int * tableau1; int * tableau2; }; |

Le probl�me est que si une exception est lev�e lors de la construction d'un objet, c'est donc que celle-ci a �chou�, et donc que l'objet n'est pas cr��. Comme il n'est pas cr��, il n'a pas � �tre d�truit, et donc son destructeur ne sera pas appel�. Autrement dit, si une exception est lev�e dans le constructeur d'un objet, son destructeur ne sera pas appel�.
Il faut donc toujours s'assurer que le code contenu dans le constructeur est exception safe, c'est-�-dire qu'il r�siste aux exceptions en ne provoquant pas de pertes de ressources. Dans notre exemple pr�c�dent cela signifie qu'il faut g�rer l'exception bad_alloc de cette mani�re :
Code c++ : | S�lectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | class Test { public: // exception bad_alloc en cas de m�moire insuffisante Test( int A, int B ) : tableau1( 0 ) { // ici tableau1 vaut NULL, donc en cas d'�chec d'allocation // on peut appeler delete [] sans probl�me try { this->tableau1 = new int[ A ]; this->tableau2 = new int[ B ]; } catch ( const std::bad_alloc & ) { // tableau2 n'a pas �t� allou� quoi qu'il arrive // lib�rer tableau1 s'il a pu �tre allou� delete [] this->tableau1; // relancer l'exception throw; } } ~Test() { delete [] this->tableau2; delete [] this->tableau1; } private: int * tableau1; int * tableau2; }; |

Il est � noter que si en cas d'exception dans le constructeur le destructeur n'est pas appel�, tous les membres construits jusqu'au point de l'exception sont quant � eux bien d�truits.
Il est possible de lever une exception dans un destructeur, mais c'est extr�mement d�conseill� et consid�r� comme une tr�s mauvaise pratique. La raison en est simple : si la destruction d'un objet �choue, que faut-il faire ? Mais aussi un autre probl�me plus grave peut appara�tre. Lorsqu'une exception est lev�e, la pile des appels est remont�e (on parle de stack unwinding ou d�roulage de la pile) et � cette occasion les objets locaux de la fonction que l'on s'appr�te � quitter sont d�truits. Donc leur destructeur respectif est appel�. Si l'un d'entre eux vient � lever une exception, la situation devient alors tr�s complexe : laquelle des deux exceptions faut-il g�rer ? N'oubliez pas qu'� ce moment nous ne sommes toujours pas dans un bloc catch, mais en train de nous y rendre en quittant les fonctions appel�es qui nous en s�pare.
Or, en quittant l'une d'entre elles, on d�truit un objet qui lance une nouvelle exception, et nous nous retrouvons alors avec deux exceptions � traiter en m�me temps. La situation �tant insoluble, la norme d�finit que la fonction standard terminate est appel�e dans un tel cas, ce qui provoque la fin brutale du programme.
Pour cette tr�s bonne raison, il est important que les destructeurs ne l�vent jamais d'exceptions. On peut s'en assurer en appelant uniquement des fonctions n'�chouant jamais (voir Comment indiquer qu'une fonction ne l�ve jamais d'exception�?).
Il est possible d'utiliser noexcept pour le faire, et il est particuli�rement recommand� de le faire pour certaines op�rations comme les constructeurs par d�placement [Ajouter un lien vers une nouvelle entr�e de la faq � cr�er sur le sujet].
Cette d�claration peut s'utiliser de mani�re simple ou conditionnelle :
Code : | S�lectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | void f1() noexcept // Cette fonction ne lance pas d'exceptions { //... } void f2() noexcept(true) // Cette fonction non plus { //... } void f3() noexcept(noexcept(g) && noexcept(h)) // Cette fonction ne lance pas d'exceptions si g et h sont aussi d�clar�s noexcept { //... } |
Certaines fonctions sont par d�faut noexcept : Les destructeurs et les operator delete.
� noter : Les sp�cifications d�exceptions sont d�pr�ci�es, et leur usage n'est pas recommand�.
Il n'y a pas de moyen direct. Utiliser une sp�cification d'exception vide permet de simuler �a, et certains compilateurs consid�rent ce cas un peu diff�remment d'une sp�cification d'exception non vide:
Code : | S�lectionner tout |
1 2 3 | void Test() throw () { } |
Dans certains langages (tels que Java ou C#), il est possible de cr�er un bloc finally � la suite d'un bloc try...catch afin de s'assurer qu'une op�ration (de lib�ration de ressource par exemple) soit bien effectu�e. Par exemple, en C++ on ne peut pas �crire ceci :
Code c++ : | S�lectionner tout |
1 2 3 4 5 6 7 8 9 10 | char * buffer = new char[ 100 ]; try { // op�ration susceptible de lever une exception } finally { // s'assurer que la m�moire est lib�r�e delete [] buffer; } |
Code c++ : | S�lectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | char * buffer = new char[ 100 ]; try { // op�ration susceptible de lever une exception } catch ( ... ) { // �viter les fuites de m�moire delete [] buffer; // relancer l'exception throw; } // tout s'est bien pass�, lib�rer la m�moire delete [] buffer; |
Ce principe s'appelle le RAII, et est d�velopp� dans la question

Proposer une nouvelle r�ponse sur la FAQ
Ce n'est pas l'endroit pour poser des questions, allez plut�t sur le forum de la rubrique pour �aLes sources pr�sent�es sur cette page sont libres de droits et vous pouvez les utiliser � votre convenance. Par contre, la page de pr�sentation constitue une �uvre intellectuelle prot�g�e par les droits d'auteur. Copyright � 2025 Developpez Developpez LLC. Tous droits r�serv�s Developpez LLC. Aucune reproduction, m�me partielle, ne peut �tre faite de ce site et de l'ensemble de son contenu : textes, documents et images sans l'autorisation expresse de Developpez LLC. Sinon vous encourez selon la loi jusqu'� trois ans de prison et jusqu'� 300 000 � de dommages et int�r�ts.