42 votes

Conception de classe d'exception c ++

Qu'est ce qu'un bon design pour un ensemble de classes d'exception? Je vois toutes sortes de choses autour de quelles classes d'exception qui devrait et ne devrait pas le faire, mais pas une conception simple qui est facile à utiliser et à étendre qui fait ces choses.

  1. Les classes d'exception ne devrait pas lancer des exceptions, car cela pourrait l'amener tout droit à la résiliation du processus sans aucune possibilité d'enregistrer l'erreur etc.
    • Il doit être possible d'obtenir une interface utilisateur conviviale de la chaîne, de préférence localisée à leur langue, de sorte qu'il ya quelque chose à leur dire avant de fermer l'application elle-même si elle ne peut pas récupérer d'une erreur.
    • Il doit être possible d'ajouter des informations comme la pile se déroule, par exemple, si un analyseur xml ne parvient pas à analyser un flux d'entrée, pour être en mesure d'ajouter que la source était à partir d'un fichier, ou sur le réseau, etc.
    • Les gestionnaires d'Exception besoin d'accéder facilement à l'information dont ils ont besoin pour gérer l'exception
    • Écrire formaté à l'exception des informations dans un fichier journal (En anglais, pas de traduction ici).

1 et 4, de travailler ensemble est le plus gros problème que je vais avoir, depuis la mise en forme et de sortie de fichier méthodes pourraient éventuellement échouer.

EDIT: Donc, après avoir regardé des classes d'exception en plusieurs classes, et aussi dans la question Neil liés à, il semble être de pratique courante, à juste complètement ignorer le point 1 (et donc le coup de pouce prestations de conseil), ce qui semble être une assez mauvaise idée de moi.

De toute façon j'ai pensé id de poste de la classe d'exception, je suis en train de penser à l'aide.

class Exception : public std::exception
{
public:
    //enum for each exception type, which can also be used to determin
    //exception class, useful for logging or other localisation methods
    //for generating a message of some sort.
    enum ExceptionType
    {
        //shouldnt ever be thrown
        UNKNOWN_EXCEPTION = 0,
        //same as above but has a string that may provide some info
        UNKNOWN_EXCEPTION_STR,
        //eg file not found
        FILE_OPEN_ERROR,
        //lexical cast type error
        TYPE_PARSE_ERROR,
        //NOTE: in many cases functions only check and throw this in debug
        INVALID_ARG,
        //an error occured while trying to parse data from a file
        FILE_PARSE_ERROR,
    }
    virtual ExceptionType getExceptionType()const throw()
    {
        return UNKNOWN_EXCEPTION;
    }
    virtual const char* what()throw(){return "UNKNOWN_EXCEPTION";}
};
class FileOpenError : public Exception
{
public:
    enum Reason
    {
        FILE_NOT_FOUND,
        LOCKED,
        DOES_NOT_EXIST,
        ACCESS_DENIED
    };
    FileOpenError(Reason reason, const char *file, const char *dir)throw();
    Reason getReason()const throw();
    const char* getFile()const throw();
    const char* getDir ()const throw();
private:
    Reason reason;
    static const unsigned FILE_LEN = 256;
    static const unsigned DIR_LEN  = 256;
    char file[FILE_LEN], dir[DIR_LEN];
};

Le Point 1 est réglé, étant donné que toutes les chaînes sont gérées par la copie d'un interne, fixe la taille de la mémoire tampon (troncature si nécessaire, mais toujours une valeur null).

Bien que cela n'a pas d'adresse point 3, mais je pense que le point est plus que probable de l'utilisation limitée dans le monde réel, de toute façon, et ils sont les plus susceptibles d'être abordés en lançant une nouvelle exception en cas de besoin.

25voto

Adrian McCarthy Points 17018

Utiliser une faible profondeur de la hiérarchie de classes d'exception. Prise de la hiérarchie trop profond ajoute plus de complexité que de valeur.

Déduire l'exception des classes de std::exception (ou l'une de l'autre, les exceptions standard comme std::runtime_error). Cela permet génériques, les gestionnaires d'exception au plus haut niveau pour traiter toutes les exceptions que vous n'avez pas. Par exemple, il pourrait y avoir un gestionnaire d'exception que les journaux d'erreurs.

Si c'est pour une bibliothèque ou d'un module, vous pouvez choisir une base spécifiques à votre module (dérivées de l'une des classes d'exception). Les appelants peuvent décider d'attraper quoi que ce soit à partir de votre module de cette façon.

Je ne voudrais pas faire trop de classes d'exception. Vous pouvez emballer beaucoup de détails à propos de l'exception dans la classe, de sorte que vous n'avez pas nécessairement besoin de faire une exception de la classe pour chaque type d'erreur. De l'autre côté, vous ne voulez classes uniques pour les erreurs que vous attendez pour gérer. Si vous faites un analyseur, vous pourriez avoir un seul syntax_error exception avec des membres qui décrivent les détails du problème plutôt que d'un tas de spécialité pour différents types d'erreurs de syntaxe.

Les chaînes dans les exceptions sont là pour le débogage. Vous ne devriez pas les utiliser dans l'interface utilisateur. Vous voulez garder de l'INTERFACE utilisateur et la logique distincte que possible, pour permettre à des choses comme la traduction vers d'autres langues.

Votre classes d'exception peuvent avoir des champs supplémentaires avec des détails sur le problème. Par exemple, un syntax_error exception pourrait avoir le nom du fichier source, le numéro de ligne, etc. Autant que possible, s'en tenir à des types de base de ces champs pour réduire les risques de la construction ou de la copie de l'exception pour déclencher une autre exception. Par exemple, si vous devez stocker un nom de fichier dans l'exception, vous pourriez vouloir un simple tableau de caractères de longueur fixe, plutôt qu'un std::string. Typique des implémentations de std::exception d'allouer dynamiquement de la chaîne de motif à l'aide de malloc. Si le malloc échoue, ils sont prêts à sacrifier la chaîne de raison plutôt que de jeter une exception imbriquée ou de s'écraser.

Les Exceptions en C++ doit être pour "exceptionnelle" des conditions. Donc, l'analyse des exemples pourraient ne pas être les bonnes. Une erreur de syntaxe rencontré lors de l'analyse d'un fichier ne peut pas être spécial suffisamment pour justifier d'être manipulés par des exceptions. Je dirais quelque chose qui est exceptionnel, si le programme ne peut probablement pas continuer à moins que la condition est explicitement traitée. Ainsi, la plupart des échecs d'allocation de mémoire sont exceptionnelles, mais la mauvaise qualité des entrées de l'utilisateur n'est probablement pas.

8voto

jon-hanson Points 3912

Utilisez l'héritage virtuel . Cette idée est due à Andrew Koenig. L'utilisation de l'héritage virtuel à partir de la ou des classes de base de votre exception évite les problèmes d'ambiguïté sur le site de capture au cas où quelqu'un lève une exception dérivée de plusieurs bases ayant une classe de base en commun.

Autres conseils tout aussi utiles sur le site boost

7voto

grimner Points 416


2: Non, vous ne devez pas mélanger de l'interface utilisateur (=messages localisés) avec la logique du programme. La Communication de l'utilisateur doit être fait au niveau externe lors de l'application on sait qu'il ne peut pas traiter la question. La plupart des informations dans un exception est trop d'un détail d'implémentation pour montrer à l'utilisateur de toute façon.
3: Utiliser boost.exception pour ce
5: Pas de ne pas le faire. Voir les 2. La décision de journal doit toujours être une erreur de manipulation de site.

Ne pas utiliser un seul type d'exception. Utiliser suffisamment de types, de sorte que l'application peut utiliser un gestionnaire catch pour chaque type d'erreur de récupération nécessaire

4voto

Michael Burr Points 181287

Pas directement lié à la conception d'une hiérarchie de classes d'exceptions, mais il est important (et lié à l'utilisation de ces exceptions) que vous devriez généralement jeter par valeur et attraper par référence.

Cela évite les problèmes liés à la gestion de la mémoire de l'exception levée (si vous jetez des pointeurs) et au potentiel de découpage d'objet (si vous attrapez des exceptions par valeur).

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