120 votes

Dois-je utiliser un spécificateur d'exception en C++ ?

En C++, vous pouvez spécifier qu'une fonction peut ou non lancer une exception en utilisant un spécificateur d'exception. Par exemple :

void foo() throw(); // guaranteed not to throw an exception
void bar() throw(int); // may throw an exception of type int
void baz() throw(...); // may throw an exception of some unspecified type

J'ai des doutes quant à leur utilisation réelle pour les raisons suivantes :

  1. Le compilateur n'applique pas vraiment les spécificateurs d'exception de manière rigoureuse, de sorte que les avantages ne sont pas très importants. Idéalement, vous voudriez obtenir une erreur de compilation.
  2. Si une fonction viole un spécificateur d'exception, je pense que le comportement standard est de terminer le programme.
  3. Dans VS.Net, il traite throw(X) comme throw(...), donc l'adhésion à la norme n'est pas forte.

Pensez-vous que les spécificateurs d'exception devraient être utilisés ?
Veuillez répondre par "oui" ou "non" et fournir quelques raisons pour justifier votre réponse.

95voto

Christopher Points 6688

Non.

En voici quelques exemples :

  1. Il est impossible d'écrire du code modèle avec des spécifications d'exception,

    template<class T>
    void f( T k )
    {
         T x( k );
         x.x();
    }

    Les copies peuvent être rejetées, le passage de paramètres peut être rejeté, et x() pourrait lancer une exception inconnue.

  2. Les spécifications d'exception ont tendance à interdire l'extensibilité.

    virtual void open() throw( FileNotFound );

    pourrait se transformer en

    virtual void open() throw( FileNotFound, SocketNotReady, InterprocessObjectNotImplemented, HardwareUnresponsive );

    Vous pourriez vraiment écrire ça comme

    throw( ... )

    La première n'est pas extensible, la deuxième est trop ambitieuse et la troisième est vraiment ce que vous voulez dire, lorsque vous écrivez des fonctions virtuelles.

  3. Code hérité

    Lorsque vous écrivez un code qui repose sur une autre bibliothèque, vous ne savez pas vraiment ce qu'elle peut faire si quelque chose se passe mal.

    int lib_f();
    
    void g() throw( k_too_small_exception )
    { 
       int k = lib_f();
       if( k < 0 ) throw k_too_small_exception();
    }

    g prendra fin, lorsque lib_f() jette. Ce n'est (dans la plupart des cas) pas ce que vous voulez vraiment. std::terminate() ne devrait jamais être appelé. Il est toujours préférable de laisser l'application se planter avec une exception non gérée, à partir de laquelle vous pouvez récupérer un suivi de pile, plutôt que de mourir silencieusement/violemment.

  4. Écrivez du code qui renvoie des erreurs courantes et qui lance des requêtes lors d'occasions exceptionnelles.

    Error e = open( "bla.txt" );
    if( e == FileNotFound )
        MessageUser( "File bla.txt not found" );
    if( e == AccessDenied )
        MessageUser( "Failed to open bla.txt, because we don't have read rights ..." );
    if( e != Success )
        MessageUser( "Failed due to some other error, error code = " + itoa( e ) );
    
    try
    {
       std::vector<TObj> k( 1000 );
       // ...
    }
    catch( const bad_alloc& b )
    { 
       MessageUser( "out of memory, exiting process" );
       throw;
    }

Néanmoins, lorsque votre bibliothèque ne fait que lancer ses propres exceptions, vous pouvez utiliser les spécifications des exceptions pour indiquer votre intention.

42voto

Michael Burr Points 181287

Évitez les spécifications d'exception en C++. Les raisons que vous donnez dans votre question sont un assez bon début pour expliquer pourquoi.

Voir l'article d'Herb Sutter "Un regard pragmatique sur les spécifications des exceptions" .

14voto

Loki Astari Points 116129

Je pense que la convention standard exceptée (pour C++)
Les spécificateurs d'exception étaient une expérience dans la norme C++ qui a surtout échoué.
L'exception étant que le spécificateur no throw est utile mais vous devez également ajouter le bloke try catch approprié en interne pour vous assurer que le code correspond au spécificateur. Herb Sutter a une page sur le sujet. Gotch 82

En complément, je pense qu'il est utile de décrire les garanties d'exception.

Il s'agit essentiellement de documentation sur la façon dont l'état d'un objet est affecté par des exceptions échappant à une méthode sur cet objet. Malheureusement, ils ne sont pas appliqués ou autrement mentionnés par le compilateur.
Boost et exceptions

Garanties exceptionnelles

Aucune garantie :

Il n'y a aucune garantie sur l'état de l'objet après qu'une exception échappe à une méthode.
Dans ces situations, l'objet ne doit plus être utilisé.

Garantie de base :

Dans presque toutes les situations, cela devrait être la garantie minimale offerte par une méthode.
Cela garantit que l'état de l'objet est bien défini et qu'il peut toujours être utilisé de manière cohérente.

Garantie forte : (alias garantie transactionnelle)

Cela garantit que la méthode sera complètement réussie
Sinon, une exception sera levée et l'état des objets ne sera pas modifié.

Pas de garantie de jet :

La méthode garantit qu'aucune exception n'est autorisée à se propager hors de la méthode.
Tous les destructeurs devraient apporter cette garantie.
| N.B. Si une exception échappe à un destructeur alors qu'une exception est déjà en train de se propager
| l'application se termine

8voto

Jeremy Points 1287

Gcc émettra des avertissements lorsque vous violez les spécifications des exceptions. Ce que je fais, c'est d'utiliser des macros pour utiliser les spécifications d'exception uniquement dans une compilation en mode "lint", expressément pour vérifier que les exceptions sont en accord avec ma documentation.

7voto

Harold Ekstrom Points 1214

Le seul spécificateur d'exception utile est "throw()", comme dans "doesn't throw".

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X