为什么派生类异常可以被基类捕获子句捕获
Why derived class exception could be caught by base class catch clause.
我尝试过在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 的复制构造函数接受可以匹配 B 类型对象的const A&
,并且复制构造函数隐式地将数据类型从 B 转换为 A。为了确认这一点,我为 struct A
添加了复制构造函数:
A( const A& other ) : m_value( other.m_value ) {
std::cout << "hellon";
}
这在执行catch( A a )
时确实输出 hello ,但是当我显式定义复制构造函数时,如下所示:
explicit A( const A& other ) : m_value( other.m_value ) {
std::cout << "hellon";
}
编译器大喊"没有匹配的构造函数来初始化'A'"。
我不知道为什么。为什么不直接跳到catch( B b )
?
[except.handle]/4 中涵盖了这个确切的问题:
try 块的处理程序按出现顺序进行尝试。这使得编写处理程序成为可能 永远不能执行,例如,通过将派生类的处理程序放在相应 基类。
如果您在启用警告的情况下进行编译,这也将变得清晰:
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
,但这不是异常逻辑处理的作用。它只是检查类型。根据 [except.handle]/3:
处理程序是类型
E
的异常对象的匹配项,如果
— [...]
— 处理程序的类型为 cvT
或 cvT&
T
是E
的明确公共基类,或
— [...]
在我们的例子中,A
是一个明确的 B
公共基类,处理程序的类型是 A
,因此处理程序匹配。句点。现在,事实证明我们实际上无法使用处理程序,因此代码格式不正确。
因为catch (A ...)
是第一位的。这是C++的一个错误特征。理想情况下,它会给你一个编译错误,因为catch
块的顺序不对,或者至少是无法访问的代码。把catch (B ...)
放在第一位。
- std::具有相同基类的类的变体
- 是否可以初始化不可复制类型的成员变量(或基类)
- 在C++中,是否可以基于给定的标识符创建基类的新实例,反之亦然
- 基类中的函数名称解析
- C++初始化基类
- 如何通过派生类函数更改基类中的向量
- 如何定义一个纯抽象基类
- 如何使用基类指针引用派生类成员
- 继承:构造函数,初始化C++11中基类的类C数组成员
- 使用基类指针创建对象时,缺少派生类析构函数
- 如何引用基类的派生类?
- 如果基类包含双指针成员,则派生类的构造函数
- 在模板基类中为继承类中的可选重写生成虚拟方法
- 为什么此派生对象无法访问基类的后递减方法?
- 公开最直接的基类模板名称
- 当基类是依赖类型时,这是一个缺陷吗
- 如何使基类的运算符对基类的可变参数数可见(请参阅下面的代码)?
- 模板基类中的静态变量
- 为什么派生类异常可以被基类捕获子句捕获
- 在基类子句中使用类中定义的typedefs/type别名