我尝试过在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 ...)
放在第一位。