C++ 自定义异常层次结构。来自std::exception和std::bad_alloc的可怕钻石

C++ 自定义异常层次结构。来自std::exception和std::bad_alloc的可怕钻石,c++,exception,c++11,polymorphism,multiple-inheritance,C++,Exception,C++11,Polymorphism,Multiple Inheritance,我自己的异常层次结构中的继承有问题 类Exception具有非常好的功能(回溯、日志记录等),因此它是任何异常的基类。它继承自std::exception,正如我在许多网页中看到的那样。另外,我正在使用一个单元测试框架,它报告任何意外抛出的std::exception。但最后一句话,这只是为了方便 然后,我有一个新的OutOfMemoryException类,它将由一个自定义的新\u处理程序抛出。此类继承自Exception,但也继承自std::bad_alloc,以与现有代码兼容。我想这更重要

我自己的异常层次结构中的继承有问题

Exception
具有非常好的功能(回溯、日志记录等),因此它是任何异常的基类。它继承自
std::exception
,正如我在许多网页中看到的那样。另外,我正在使用一个单元测试框架,它报告任何意外抛出的
std::exception
。但最后一句话,这只是为了方便

然后,我有一个新的
OutOfMemoryException
类,它将由一个自定义的新\u处理程序抛出。此类继承自
Exception
,但也继承自
std::bad_alloc
,以与现有代码兼容。我想这更重要,因为
new
将不再抛出
std::bad_alloc

这里的问题是显而易见的:由于
std::bad_alloc
源于
std::exception
,我有一个可怕的菱形情况

类异常:公共std::异常{};
类OutOfMemoryException:public异常,public std::bad_alloc{};
不幸的是,正如您在这里看到的,标准异常并不是虚拟继承,因此
std::exception
是一个模棱两可的基类

然后:

  • 有没有可能以任何方式解决这个问题?(我不知道,任何类或模板技巧)

  • 如果不是,则最好是
    Exception
    不继承自
    std::Exception
    OutOfMemoryException
    不继承自
    std::bad\u alloc

  • 我可以破解单元测试框架,因为它有许可证


    提前谢谢你,对不起,我的英语说得不是很好。

    这并不是说这个解决方案很好,但它可能足以满足你的需要。 假设
    std::exception
    定义了一个名为
    func1()
    的函数。当然,您将从继承中的两个路径获得它,对
    OutOfMemoryException::func1()
    的任何调用都会给您一条关于不明确基类的编译时消息

    这是一个老套而不太漂亮的解决方案。覆盖
    std::exception
    中的非虚拟函数,只需转发任一版本。
    Exception::func1()
    std::bad\u alloc::func1()


    我确实意识到这很难看,因为您正在重写非虚拟函数。如果被重写的函数采用参数,那么您还将面临完美的转发问题。如果不小心,可能会复制不想复制的内容,等等。但它应该允许您从std::exception调用成员函数。

    想到的一件事是,您可以使用(以及其他错误可能导致的其他异常类,例如运行时异常
    from
    runtime\u error`):

    这确实不是一个很好的解决方案,因为当您有一个以上的继承级别时,会丢失一些多态属性并很容易变得复杂,但它至少可以工作

    在实现方面,另一个稍微复杂一些的解决方案是将整个过程重构为两个不同的继承链,并使用伪类将这两个链组合起来:

    class ExceptionT {};
    class OutOfMemoryT: public ExceptionT {};
    class Exception:   public ExceptionT, public std::exception {};
    class OutOfMemory: public OutOfMemoryT, public std::bad_alloc {};
    
    这样,您将抛出
    OutOfMemory
    并捕获
    OutOfMemory
    std::bad_alloc


    这两种方法都使得在处理程序中从一个异常链(
    Exception/std::Exception
    )转换到另一个异常链变得更加困难,但我认为这可能没有那么重要(因为
    std::Exception
    除了作为标准之外没有提供太多功能,您可能总是更愿意捕获异常).

    有一种方法可以使用额外的异常功能,但不必创建另一个异常层次结构。即,您可以使用额外的位抛出标准异常:

    struct MyExceptionInfo { /* your extra functionality goes here */};
    
    template<class T>
    struct Ex : T, MyExceptionInfo
    {
        template<class A1, class... Args>
        Ex(A1&& a1, Args&&... args)
            : T(std::forward<A1>(a1))
            , MyExceptionInfo(std::forward<Args>(args)...)
        {}
    };
    
    int main() {
        try {
            throw Ex<std::runtime_error>("oops");
        }
        catch(std::exception &e) {
            if(MyExceptionInfo* my_info = dynamic_cast<MyExceptionInfo*>(&e)) {
                // use MyExceptionInfo here
            }
        }
    }
    
    struct MyExceptionInfo{/*您的额外功能放在这里*/};
    模板
    结构例:T,MyExceptionInfo
    {
    模板
    Ex(A1和A1,参数和…参数)
    
    :T(std::forward library for inspiration.

    FWIW您的类不使用虚拟继承either@R.MartinhoFernandes我的类没有使用它,因为任何虚拟继承的组合都不能解决这个问题。最大的问题是当我抛出一个
    OutOfMemoryException
    并试图捕获
    std::exception
    ,因为它是一个模棱两可的基类,向上转换不起作用,它转到
    catch(…)
    std::terminate
    。我明白了。这是个问题。你不能让其中一个基不是基而是成员吗?然后通过包装器提供它独有的功能。当然,在你的情况下,如果你想能够捕获
    OutOfMemoryException
    作为
    异常和
    std::bad_alloc
    以及
    std::exception
    ,那么这将不起作用。但是如果您可以选择其中一个与
    std::exception
    一起,那么您应该可以。在我的
    exception
    类中,没有真正的必要继承
    std::exception
    。这只是为了方便起见,因为单元测试框架正在捕获
    std::exception
    。我想
    OutOfMemoryException
    必须源自
    std::bad_alloc
    ,因为当我在
    new_handler
    +1中替换它时,它可能会破坏现有代码。第一个解决方案应该足够了;它不会丢失任何多态性。
    OutOfMemoryException
    不应该继承自
    Excep尽管如此,
    Exception
    专门化根本不应该从中继承。(注意,您编写的
    public ExceptionBase
    没有意义,因为
    ExceptionBase
    不是一个模板)。@Potatoswatter:感谢您捕获到了
    class ExceptionT {};
    class OutOfMemoryT: public ExceptionT {};
    class Exception:   public ExceptionT, public std::exception {};
    class OutOfMemory: public OutOfMemoryT, public std::bad_alloc {};
    
    struct MyExceptionInfo { /* your extra functionality goes here */};
    
    template<class T>
    struct Ex : T, MyExceptionInfo
    {
        template<class A1, class... Args>
        Ex(A1&& a1, Args&&... args)
            : T(std::forward<A1>(a1))
            , MyExceptionInfo(std::forward<Args>(args)...)
        {}
    };
    
    int main() {
        try {
            throw Ex<std::runtime_error>("oops");
        }
        catch(std::exception &e) {
            if(MyExceptionInfo* my_info = dynamic_cast<MyExceptionInfo*>(&e)) {
                // use MyExceptionInfo here
            }
        }
    }