为什么派生类异常可以被基类catch子句捕获。

时间:2016-03-23 01:05:16

标签: c++ exception

我已尝试在Xcode上使用代码

#include <iostream>


/*Exceptions*/
struct A {
    A( int value ) : m_value( value ) {}
    int m_value;
};

struct B : A {
    B( int value ) : A( value ) {}
};
//+++++++++++++++++++++++++++++++

/*Exceptions End*/

int main(int argc, const char * argv[]) {

    try {
        try {
            throw B( 5 );
        }

        catch ( A a ) {
            a.m_value *= 2;
        }
        catch ( B b ) {
            b.m_value -= 2;
            throw b;
        }

    }
    catch ( A a ) {
        std::cout << a.m_value;
    }
    return 0;
}

这里抛出的异常类型是B,但是catch( A a )抓住了B.

我对此有所考虑,但不知道它是否正确。我认为这是因为A的复制构造函数接受const A&可以匹配B类型对象,复制构造函数隐式地将数据类型从B转换为A.为了确认这一点,我为{{1}添加了复制构造函数}}:

struct A

执行A( const A& other ) : m_value( other.m_value ) { std::cout << "hello\n"; } 时会输出hello,但是当我明确定义复制构造函数时,如下所示:

catch( A a )

编译器叫喊&#34;没有匹配的构造函数用于初始化&#39; A&#39;&#34;。

我不知道为什么。为什么它没有跳转到explicit A( const A& other ) : m_value( other.m_value ) { std::cout << "hello\n"; }

2 个答案:

答案 0 :(得分:2)

[except.handle]/4

中涵盖了这个确切的问题
  

尝试块的处理程序按外观顺序进行尝试。这使得编写处理程序成为可能   永远不能执行,例如通过在对应的处理程序之后放置派生类的处理程序   基类。

如果编译时启用了警告,那么这也会变得清晰:

main.cpp:28:9: warning: exception of type 'B' will be caught
         catch ( B b ) {
         ^
main.cpp:24:9: warning:    by earlier handler for 'A'
         catch ( A a ) {
         ^

所以是的,最终发生的事情是抛出一次B,我们只是逐个查看处理程序列表。我们能赶上A吗?我们可以! B可转换为A

现在,当你制作A的复制构造函数时,会发生一些有趣的事情。您无法将B隐式转换为A,但这不是异常逻辑处理的作用。它只是检查类型。根据{{​​3}}:

  

处理程序是类型E的异常对象的匹配,如果是    - [...]
   - 处理程序的类型为 cv T cv T&,而TE的明确公共基类}或者
   - [...]

在我们的例子中,AB的明确公共基类,处理程序的类型为A,因此处理程序匹配。完全停止。现在,事实证明我们实际上使用处理程序,因此代码格式不正确。

答案 1 :(得分:0)

因为catch (A ...)是第一位的。这是C ++的错误。理想情况下,它会给你一个无序的catch块的编译错误,或至少是无法访问的代码。先放catch (B ...)