1.4 异常匹配
1. 异常匹配不支持非派生的隐式转换
在下面的例子中,2类能隐式转换为1类,但是在异常捕获的时候,不会。
#include <iostream> using namespace std; class Except1 { }; class Except2 { public: Except2(const Except1 &) {} };
void f() { throw Except1(); }
int main(int argc, const char **argv) { try { f(); } catch (Except2 &) { cout << "inside catch(Except2)" << endl; } catch (Except1 &) { cout << "inside catch(Except1)" << endl; } return 0; }
|
2. 异常匹配在派生处理的做法
由于异常匹配允许基类匹配派生类,因此优先处理派生类异常,将基类异常放在最后,下面演示了反例后果。
#include <iostream> using namespace std;
class X { public: class Trouble {}; class Small : public Trouble {}; class Big :public Trouble {}; void f() { throw Big(); } };
int main(int argc, const char** argv) { X x; try{ x.f(); }catch(X::Trouble&){ cout<<"catch Trouble"<<endl; }catch(X::Big&){ cout<<"catch Big trouble"<<endl; }catch(X::Small&){ cout<<"catch Small Trouble"<<endl; } return 0; }
|
另外,异常处理使用引用处理,防止派生类信息被基类捕获后消失。
3. 捕获所有异常
使用省略号代替异常参数列表,就能捕获所有异常,但是不能获得异常实际的信息。一般放在异常捕获的最后,用于捕获意料之外的异常,防止程序直接崩溃。
catch(...) { cout<<"an exception was thrown"<<endl; }
|
4. 重新抛出异常
有时,捕获异常并不希望在此处进行处理,而是为了封装一层资源释放,将接收的异常抛向更高一级来处理(省略号捕获符合这种场景)。此时,在catch块中使用不带参数的throw来重新抛出异常。
5. 不捕获异常情况
在层层捕获后,依然逃逸的异常。
1. terminate()函数
最终没有被捕获的异常,会导致库函数terminate()被调用,默认调用C库标准abort()退出。
注意:abort()绕过析构函数直接退出。
在下列两种情况下,terminate()函数也会执行:
2. set_terminate()函数
通过调用set_terminate()函数,来更改指向terminate()的函数指针。同时返回一个原terminate()函数指针便于恢复。
设置自定义terminate函数的要求:
-
无参数
-
无返回值
-
不能抛出异常
-
必须执行某种方式的程序终止逻辑
案例
下面的案例,演示了在异常处理过程中,throw时调用类析构函数,函数却再次抛出异常,导致栈反解,直接进入terminate()函数,从而绕开catch(…)的情况。
#include <iostream> #include <exception> using namespace std;
void terminator() { cout << "I'll be back!" << endl; exit(0); }
void (*old__terminate)() = set_terminate(terminator);
class Botch { public: class Fruit { }; void f() { cout << "Botch::f()" << endl; throw Fruit(); } ~Botch() { throw 'c'; } };
int main() { try { Botch b; b.f(); } catch (...) { cout << "inside catch(...)" << endl; } }
|