Question

En C ++, vous pouvez spécifier qu'une fonction peut ou non générer une exception à l'aide d'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

Je doute de les utiliser réellement pour les raisons suivantes:

  1. Le compilateur n'applique pas de manière rigoureuse les spécificateurs d'exception, de sorte que les avantages ne sont pas énormes. Idéalement, vous voudriez avoir une erreur de compilation.
  2. Si une fonction viole un spécificateur d'exception, je pense que le comportement standard consiste à terminer le programme.
  3. Dans VS.Net, le jet (X) est traité comme un jet (...), de sorte que le respect de la norme n’est pas strict.

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

Était-ce utile?

La solution

Non.

Voici plusieurs exemples pour lesquels:

  1. Le code de modèle est impossible à écrire avec les spécifications d'exception,

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

    Il est possible que les copies jettent, le paramètre passant puisse être lancé, et x () jette une exception inconnue.

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

    virtual void open() throw( FileNotFound );
    

    pourrait évoluer vers

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

    Vous pouvez vraiment écrire cela comme

    throw( ... )
    

    Le premier n'est pas extensible, le second est trop ambitieux et le troisième est vraiment ce que vous voulez dire, lorsque vous écrivez des fonctions virtuelles.

  3. Code hérité

    Lorsque vous écrivez du code qui repose sur une autre bibliothèque, vous ne savez pas vraiment ce qu'il peut faire en cas de problème.

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

    g se terminera lorsque lib_f () sera lancé. 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 planter avec une exception non gérée, à partir de laquelle vous pouvez récupérer une trace de pile, plutôt que de mourir silencieusement / violemment.

  4. Écrivez un code qui renvoie les erreurs courantes et les jette dans des cas exceptionnels.

    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 lève vos propres exceptions, vous pouvez utiliser des spécifications d'exception pour indiquer votre intention.

Autres conseils

Évitez les spécifications d'exception en C ++. Les raisons que vous donnez dans votre question sont un très bon début pour la raison.

Voir "Un aperçu pragmatique des spécifications des exceptions" de Herb Sutter .

Je pense que la convention standard sauf (pour C ++)
Les spécificateurs d'exception étaient une expérience dans la norme C ++ qui avait en grande partie échoué.
L'exception étant que le spécificateur no-projection est utile, mais vous devez également ajouter le bloc catch 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 outre, je pense qu’il vaut la peine de décrire les garanties d’exception.

Il s'agit essentiellement de documentation sur la manière dont l'état d'un objet est affecté par les exceptions échappant à une méthode sur cet objet. Malheureusement, ils ne sont pas appliqués ou mentionnés par le compilateur.
Amplification et exceptions

Garanties d'exception

Pas de garantie:

  

Il n'y a aucune garantie sur l'état de l'objet lorsqu'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 fournie par une méthode.
     Cela garantit que l'état de l'objet est bien défini et peut toujours être utilisé de manière cohérente.

Garantie forte: (Garantie Transactionnelle)

  

Ceci garantit que la méthode aboutira avec succès
     Ou une exception sera levée et l'état des objets ne changera pas.

Aucune garantie de projection:

  

La méthode garantit qu'aucune exception n'est autorisée à se propager en dehors de la méthode.
     Tous les destructeurs doivent faire cette garantie.
     | N.B. Si une exception échappe à un destructeur alors qu'une exception se propage déjà
     | l'application va se terminer

gcc émettra des avertissements lorsque vous ne respectez pas les spécifications des exceptions. Ce que je fais est d’utiliser des macros pour utiliser les spécifications d’exception uniquement dans un "lint". Le mode compile expressément pour vérifier que les exceptions concordent avec ma documentation.

Le seul spécificateur d'exception utile est "throw ()", comme dans "ne lance pas".

Non. Si vous les utilisez et qu'une exception est générée que vous n'avez pas spécifiée, que ce soit par votre code ou par le code appelé par votre code, le comportement par défaut consiste à terminer rapidement votre programme.

De plus, je pense que leur utilisation est déconseillée dans les versions actuelles du standard C ++ 0x.

Les spécifications d'exception ne sont pas des outils très utiles en C ++. Cependant, ils sont / utiles / s'ils sont combinés avec std :: Inattendu.

Dans certains projets, ce que je fais est du code avec des spécifications d’exception, puis appelez set_unexpected () avec une fonction qui lève une exception spéciale de ma propre conception. Cette exception, lors de la construction, obtient une trace (de manière spécifique à la plate-forme) et est dérivée de std :: bad_exception (pour lui permettre d'être propagée si nécessaire). Si cela provoque un appel terminate (), comme c'est généralement le cas, la trace est imprimée par what () (ainsi que l'exception d'origine qui l'a provoquée; pas difficile à trouver) et j'obtiens donc des informations sur l'emplacement de mon contrat. violée, telle que quelle exception de bibliothèque inattendue a été levée.

Si je le fais, je ne permettrai jamais la propagation des exceptions de la bibliothèque (sauf celles de std) et dériverai toutes mes exceptions de std :: exception. Si une bibliothèque décide de lancer, je vais attraper et convertir dans ma propre hiérarchie, ce qui me permet de toujours contrôler le code. Les fonctions basées sur des modèles qui appellent des fonctions dépendantes doivent éviter les spécifications d’exception pour des raisons évidentes; de toute façon, il est rare d’avoir une interface de fonction basée sur un modèle avec du code de bibliothèque (et peu de bibliothèques utilisent réellement les modèles de manière utile).

Si vous écrivez du code qui sera utilisé par des personnes qui préféreraient consulter la déclaration de la fonction plutôt que des commentaires, une spécification leur indiquera les exceptions à capturer.

Sinon, je ne trouve pas particulièrement utile d'utiliser autre chose que throw () pour indiquer qu'il ne renvoie aucune exception.

A "throw () " La spécification permet au compilateur d’effectuer certaines optimisations lors de l’analyse de flux de code s’il sait que cette fonction ne lèvera jamais d’exception (ou du moins qu’elle promet de ne jamais lever d’exception). Larry Osterman en parle brièvement ici:

http://blogs.msdn.com/larryosterman /archive/2006/03/22/558390.aspx

En général, je n’utiliserais pas de spécificateurs d’exception. Cependant, dans les cas où si une autre exception venait de la fonction en question et que le programme serait définitivement incapable de corriger , cela peut alors être utile. Dans tous les cas, veillez à documenter clairement les exceptions pouvant être attendues de cette fonction.

Oui, le comportement attendu d'une exception non spécifiée levée à partir d'une fonction avec des spécificateurs d'exception est d'appeler terminate ().

Je noterai également que Scott Meyers aborde ce sujet dans C ++ plus efficace. Ses livres Effective C ++ et More Effective C ++ sont hautement recommandés.

Oui, si vous êtes dans la documentation interne. Ou peut-être écrire une bibliothèque que d'autres utiliseront, afin qu'ils puissent dire ce qui se passe sans consulter la documentation. Lancer ou ne pas lancer peut être considéré comme faisant partie de l'API, presque comme la valeur de retour.

Je suis d'accord, ils ne sont pas vraiment utiles pour appliquer le style Java de correction du compilateur, mais c'est mieux que rien ou des commentaires désordonnés.

Ils peuvent être utiles pour les tests unitaires. Ainsi, lors de l'écriture des tests, vous savez à quoi s'attendre lorsque la fonction est lancée en cas d'échec, mais aucune application les entoure dans le compilateur. Je pense qu'ils sont du code supplémentaire qui n'est pas nécessaire en C ++. Quoi que vous choisissiez, tout ce dont vous devez être sûr est que vous respectiez la même norme de codage pour le projet et les membres de l'équipe, afin que votre code reste lisible.

Extrait de l'article:

http://www.boost.org/community/exception_safety.html

  

«Il est bien connu qu'il est impossible de   écrire un générique protégé contre les exceptions   conteneur. "Cette affirmation est souvent entendue   en référence à un article de Tom   Cargill [4] dans lequel il explore la   problème de sécurité d'exception pour un   modèle de pile générique. Dans son   article, Cargill soulève de nombreuses questions utiles   questions, mais échoue malheureusement à   présenter une solution à son problème1.   conclut en suggérant qu'un   solution peut ne pas être possible.   Malheureusement, son article a été lu par   beaucoup comme "preuve" de cette spéculation.   Depuis sa publication, il y a eu   de nombreux exemples d'exception-safe   composants génériques, parmi lesquels le C ++   Conteneurs de bibliothèque standard.

Et en effet, je peux trouver des moyens de sécuriser les exceptions de classes de modèles. À moins que vous n'ayez pas le contrôle de toutes les sous-classes, vous pouvez avoir un problème de toute façon. Pour ce faire, vous pouvez créer dans vos classes des typedefs définissant les exceptions émises par différentes classes de modèle. C’est la raison pour laquelle le problème se pose comme toujours, ensuite, au lieu de le concevoir dès le départ, et je pense que ce sont les frais généraux qui constituent le véritable obstacle.

Spécifications d'exception = ordures, demandez à tout développeur Java âgé de plus de 30 ans

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top