C ++中的自定义异常

时间:2009-10-22 08:33:09

标签: c++ stl exception multiple-inheritance

我一直在努力为我正在研究的C ++库创建一些自定义异常类。这些自定义异常捕获调试所需的额外信息,例如文件,行号等,如果出于某种原因,在测试异常时没有捕获到正确的位置。然而,大多数人似乎建议继承STL中的std :: exception类,我同意这一点,但我想知道使用多重继承来继承每个derived std::exception classes会更好(例如。 std :: runtime_error)和自定义异常类,如下面的代码所示?

另一件事,如何在异常类中进行复制构造函数和赋值运算符?他们应该被禁用吗?

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

2 个答案:

答案 0 :(得分:16)

  

我想知道使用多重继承来继承每个派生的std :: exception类会更好

请注意,这是一个问题,因为标准库中的异常非虚拟地相互派生。如果引入多重继承,则会得到没有虚拟继承的可怕菱形异常层次结构,并且无法通过std::exception&捕获派生异常,因为派生的异常类带有两个std::exception子对象,使{ {1}}“模糊的基类”。

具体例子:

std::exception

现在class my_exception : virtual public std::exception { // ... }; class my_runtime_error : virtual public my_exception , virtual public std::runtime_error { // ... }; 从({1}}两次推导(间接),一次通过my_runtime_error,一次通过std::exception。由于前者并非虚拟地来自std::run_time_error,所以

my_exception

不起作用。

编辑:

我想我已经在Stroustrup的一本书中看到了涉及MI的异常类层次结构的第一个例子,所以我得出结论,总的来说,这是一个好主意。 std lib的例外实际上并不是我认为是失败的。

当我上次设计一个异常层次结构时,我非常广泛地使用了MI,但并没有从std lib的异常类派生。在该层次结构中,您定义了抽象异常类,以便您的用户可以捕获它们,以及从这些抽象类派生的相应实现类以及实际抛出的实现基类。为了使这更容易,我定义了一些可以完成所有艰苦工作的模板:

std::exception

现在回过头来看,我想我可以让try { throw my_runtime_error(/*...*/); } catch( const std::exception& x) { // ... } 类模板派生自// 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 ); } (或std lib异常层次结构中的任何其他类,作为可选模板参数传递),因为它永远不会来自任何其他exc_impl实例。但那时候不需要这样,所以我从来没有想过。

答案 1 :(得分:12)

您应该尝试boost::exception

  

Boost Exception的目的是为了   简化异常类的设计   层次结构和帮助写   异常处理和错误报告   代码。

     

支持任意运输   数据到捕获站点,这是   由于没有投掷,否则很棘手   例外要求(15.5.1)   类型。数据可以添加到任何数据   异常对象,直接在   throw-expression(15.1),或者a   以后的时间作为例外对象   传播调用堆栈。

     

向异常添加数据的能力   它们被传递给后的对象   扔很重要,因为经常有些   需要处理的信息   例外不可用   检测到故障的上下文。

     

Boost Exception也支持   N2179式复制异常   对象,非侵入式实现   并自动由   boost :: throw_exception函数。