2009-10-22 6 views
17

J'ai essayé de créer des classes d'exceptions personnalisées pour une bibliothèque C++ sur laquelle je travaille. Ces exceptions personnalisées capturent des informations supplémentaires, telles que le fichier, le numéro de ligne, etc., nécessaires au débogage, si, pour une raison quelconque, le test d'une exception n'est pas détecté au bon endroit. Cependant la plupart des gens semblent recommander d'hériter de la classe std :: exception dans la STL, ce que je suis d'accord, mais je me demandais peut-être qu'il serait préférable d'utiliser l'héritage multiple pour hériter de chacun des derived std::exception classes (par exemple std :: runtime_error) et une classe d'exception personnalisée, comme dans le code ci-dessous? Une autre chose, comment faire pour copier des constructeurs et des opérateurs d'assignation dans des classes d'exception? Devraient-ils être désactivés?Exceptions personnalisées en C++

class Exception{ 
    public: 
     explicit Exception(const char *origin, const char *file, 
          const int line, const char *reason="", 
          const int errno=0) throw(); 

     virtual ~Exception() throw(); 

     virtual const char* PrintException(void) throw(); 

     virtual int GetErrno(void); 

    protected: 
     std::string m_origin; 
     std::string m_file; 
     int m_line; 
     std::string m_reason; 
     int m_errno; 
}; 

class RuntimeError: public virtual std::runtime_error, public Exception{ 
    public: 
       explicit RuntimeError(const char *origin, const char *file, 
            const int line, const char *reason="", 
            const int errno=0) throw(); 
     virtual ~RuntimeError() throw(); 
}; 
+0

Qu'aimeriez-vous en tirer? Pourquoi ne pas utiliser l'héritage unique? Pourquoi avez-vous besoin de personnaliser * les exceptions * Exception et RuntimeError? – jalf

+0

Je voulais utiliser la fonctionnalité de ma classe Exception personnalisée et maintenir la hiérarchie des exceptions STL en héritant des différentes classes std :: exception dérivées. De cette façon un attraper avec: try { throw RunTimeException (...); } catch (std :: runtime_error & e) { ... } catch (std :: exception & e) {...} permet également d'intercepter ma classe personnalisée. Cela pourrait être une perte de temps. Je pourrais aussi juste essayer { throw RunTimeException (...); } catch (std :: exception & e) {...} mais il pourrait alors être plus difficile d'identifier l'exception. Je ne sais pas quelle est la pratique typique lorsque les classes d'exception personnalisées? – Tom

+0

Je dirais juste hériter de std :: runtime_error (ou std :: exception). Si vous voulez un traitement spécifique, vous pouvez avoir plusieurs prises après tout. –

Répondre

11

Vous devriez essayer boost::exception

Le but de Exception Boost est de faciliter la conception des hiérarchies de classe d'exception et pour aider à écrire la gestion des exceptions et des erreurs de déclaration du code .

Il prend en charge le transport des arbitraires données sur le site de capture, ce qui est autrement difficile en raison de la non-jet exigences (15.5.1) pour exception types. Des données peuvent être ajoutées à n'importe quel objet d'exception , soit directement dans l'expression throw (15.1), soit à une date ultérieure lorsque l'objet d'exception propage la pile d'appels.

La possibilité d'ajouter des données à des objets exception après avoir été passé à jet est important, parce que souvent certains des informations nécessaires pour gérer une exception n'est pas disponible dans le contexte où la défaillance est détectée.

Exception Boost prend également en charge la copie N2179 style d'exception objets, mis en œuvre non intrusive et automatiquement par le boost :: fonction throw_exception.

+0

Je n'ai jamais utilisé boost avant. Je vais regarder dedans. Merci pour le post. – Tom

+1

Jolie bibliothèque géniale. Maintenant, j'aimerais l'avoir déjà lu! –

17

Je me demandais peut-être il serait préférable d'utiliser l'héritage multiple pour hériter de chacun des std dérivés :: classes d'exception

Notez que ceci est un problème, en raison du fait que les exceptions de la bibliothèque standard dérivent de manière non virtuelle les unes des autres. Si vous introduisez l'héritage multiple, vous obtenez la hiérarchie des exceptions de diamant redoutée sans héritage virtuel et vous ne pourrez pas capturer les exceptions dérivées par std::exception&, puisque votre classe d'exception dérivée porte deux sous-objets de std::exception, faisant de std::exception une "classe de base ambiguë".

Exemple concret:

class my_exception : virtual public std::exception { 
    // ... 
}; 

class my_runtime_error : virtual public my_exception 
         , virtual public std::runtime_error { 
    // ... 
}; 

maintenant my_runtime_error dérive (indirectement) de std::exception deux fois, une fois par std::run_time_error et une fois par my_exception.Étant donné que l'ancien ne provient pas de la quasi-std::exception, ce

try { 
    throw my_runtime_error(/*...*/); 
} catch(const std::exception& x) { 
    // ... 
} 

ne fonctionnera pas.

Edit:

Je pense que je l'ai vu le premier exemple d'une hiérarchie de classes d'exceptions impliquant MI dans l'un des livres de Stroustrup, je conclus que, en général, il est une bonne idée. Que les exceptions std lib ne dérivent pas l'une de l'autre, je considère comme un échec. Lorsque j'ai conçu une hiérarchie d'exceptions pour la dernière fois, j'ai beaucoup utilisé MI, mais je n'ai pas dérivé des classes d'exception de std lib. Dans cette hiérarchie, il y avait des classes d'exceptions abstraites que vous avez définies afin que vos utilisateurs puissent les attraper, et les classes d'implémentation correspondantes, dérivées de ces classes abstraites et d'une classe de base d'implémentation, que vous lanceriez réellement. Pour rendre cela plus facile, je définissais certains modèles qui feraient tout le travail:

// something.h 
class some_class { 
private: 
    DEFINE_TAG(my_error1); // these basically define empty structs that are needed to 
    DEFINE_TAG(my_error2); // distinguish otherwise identical instances of the exception 
    DEFINE_TAG(my_error3); // templates from each other (see below) 
public: 
    typedef exc_interface<my_error1> exc_my_error1; 
    typedef exc_interface<my_error2> exc_my_error2; 
    typedef exc_interface<my_error3,my_error2> // derives from the latter 
            exc_my_error3; 

    some_class(int i); 
    // ... 
}; 

//something.cpp 
namespace { 
    typedef exc_impl<exc_my_error1> exc_impl_my_error1; 
    typedef exc_impl<exc_my_error2> exc_impl_my_error2; 
    typedef exc_impl<exc_my_error3> exc_impl_my_error3; 
    typedef exc_impl<exc_my_error1,exc_my_error2> // implements both 
            exc_impl_my_error12; 
} 
some_class::some_class(int i) 
{ 
    if(i < 0) 
    throw exc_impl_my_error3(EXC_INFO // passes '__FILE__', '__LINE__' etc. 
          , /* ... */ // more info on error 
          ); 
} 

En regardant en arrière à ce moment, je pense que je aurais pu faire que exc_impl modèle de classe dérivent de std::exception (ou toute autre classe dans la std lib exception hierarchy, passée en paramètre de modèle optionnel), car elle ne dérive jamais d'une autre instance exc_impl. Mais à l'époque, ce n'était pas nécessaire, alors cela ne m'est jamais venu à l'esprit.

+0

+1: assez explicatif – fmuecke

+0

Merci pour le post sbi. Est-ce que faire des classes d'exception avec héritage multiple serait une bonne pratique? Ou vaut-il mieux hériter de la classe std :: exception? – Tom

+0

@Tom: J'ai ajouté mes pensées en tant que modification. – sbi