2009-10-22 30 views
17

He intentado crear algunas clases de excepciones personalizadas para una biblioteca C++ en la que estoy trabajando. Estas excepciones personalizadas capturan información adicional, como archivo, número de línea, etc., necesaria para la depuración, si por alguna razón al probar una excepción no se detecta en el lugar correcto. Sin embargo, la mayoría de las personas parecen recomendar heredar de la clase std :: exception en el STL, con lo cual estoy de acuerdo, pero me preguntaba si tal vez sería mejor usar herencia múltiple para heredar de cada uno de los derived std::exception classes (por ejemplo, std :: runtime_error) y una clase de excepción personalizada, como en el código de abajo?Excepciones personalizadas en C++

Otra cosa, ¿cómo se puede hacer para copiar constructores y operadores de asignación en clases de excepción? ¿Deberían estar deshabilitados?

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é ganarías de esto? ¿Por qué no usar herencia individual? ¿Por qué necesita personalizar las excepciones * both * Exception y RuntimeError? – jalf

+0

Quería utilizar la funcionalidad de mi clase Exception personalizada y mantener la jerarquía de excepciones STL heredando de las distintas clases derivadas std :: exception. De esta manera, un truco con: try { throw RunTimeException (...); } catch (std :: runtime_error & e) { ... } catch (std :: exception & e) {...} también captaría mi clase personalizada. Aunque podría ser una pérdida de tiempo. También podría simplemente intentar { arrojar RunTimeException (...); } catch (std :: exception & e) {...} pero podría dificultar la identificación de la excepción. No sé cuál es la práctica típica cuando las clases de excepción personalizada? – Tom

+0

Yo diría heredar de std :: runtime_error (o std :: exception). Si desea un tratamiento específico, puede tener múltiples capturas después de todo. –

Respuesta

11

Usted debe tratar boost::exception

El propósito de Excepción Boost es aliviar el diseño de clase de excepción jerarquías y para ayudar a escribir manejo de excepciones y el informe de errores código.

Es compatible con el transporte de datos arbitrarios al sitio de captura, que es de otro modo complicado debido a las no-tiro requisitos (15.5.1) para excepción tipos. Los datos se pueden agregar a cualquier objeto de excepción , ya sea directamente en throw-expression (15.1), o en un más tarde, ya que el objeto de excepción propaga la pila de llamadas.

La posibilidad de agregar datos a excepción objetos después de que hayan sido pasados ​​a tiro es importante, porque muchas veces algunos de la información necesaria para manejar una excepción no está disponible en el contexto donde se detecta el fallo.

Boost Excepción también es compatible con la copia N2179-estilo de excepción objetos, implementado de manera no intrusiva y automáticamente por la función de impulso :: throw_exception.

+0

Nunca antes había usado boost. Lo miraré. Gracias por la publicacion. – Tom

+1

biblioteca bastante impresionante. ¡Ahora desearía haberlo leído antes! –

17

Me preguntaba que tal vez sería mejor usar herencia múltiple a heredar de cada uno de los std :: derivada clases de excepción

Tenga en cuenta que este es un problema, debido al hecho de que las excepciones en la biblioteca estándar derivan virtualmente unas de otras. Si introduce herencia múltiple, obtendrá la temida jerarquía de excepciones de diamantes sin herencia virtual y no podrá detectar excepciones derivadas por std::exception&, ya que su clase de excepción derivada lleva dos subobjetos de std::exception, lo que hace que std::exception sea una "clase base ambigua".

ejemplo concreto:

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

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

ahora my_runtime_error deriva (indirectamente) de std::exception dos veces, una a través de std::run_time_error y una vez a través my_exception.Ya que el primero no se deriva de std::exception virtualmente, esta

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

no va a funcionar.

Editar:

creo que he visto el primer ejemplo de una jerarquía de clases de excepciones que implica IM en uno de los libros de BS, por lo que llegó a la conclusión de que, en general, es una buena idea. Que las excepciones de std lib no se derivan virtualmente una de la otra lo considero un fracaso.

Cuando diseñé por última vez una jerarquía de excepciones, utilicé MI muy extensamente, pero no derivé de las clases de excepción de std lib. En esa jerarquía, había clases de excepciones abstractas que usted definió para que los usuarios pudieran captarlas, y las clases de implementación correspondientes, derivadas de estas clases abstractas y de una clase base de implementación, que realmente lanzaría. Para hacer esto más fácil, he definido algunas plantillas que hacer todo el trabajo duro:

// 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 
          ); 
} 

Mirando hacia atrás en este momento, creo que podría haber hecho que exc_impl plantilla de clase se derivan de std::exception (o cualquier otra clase en el la jerarquía de excepciones de std lib, pasada como parámetro de plantilla opcional), ya que nunca se deriva de ninguna otra instancia de exc_impl. Pero en aquel entonces no era necesario, así que nunca se me ocurrió.

+0

+1: bastante explicativo – fmuecke

+0

Gracias por la publicación sbi. ¿Sería una buena práctica hacer clases de excepción con herencia múltiple? ¿O es mejor simplemente heredar de la clase std :: exception? – Tom

+0

@Tom: agregué mis pensamientos como una edición. – sbi

Cuestiones relacionadas