1-4 异常匹配

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;
}
/*output:
catch Trouble
*/

另外,异常处理使用引用处理,防止派生类信息被基类捕获后消失。

3. 捕获所有异常

使用省略号代替异常参数列表,就能捕获所有异常,但是不能获得异常实际的信息。一般放在异常捕获的最后,用于捕获意料之外的异常,防止程序直接崩溃。

catch(...)
{
cout<<"an exception was thrown"<<endl;
}

4. 重新抛出异常

有时,捕获异常并不希望在此处进行处理,而是为了封装一层资源释放,将接收的异常抛向更高一级来处理(省略号捕获符合这种场景)。此时,在catch块中使用不带参数的throw来重新抛出异常。

catch(...)
{
//clean the resource here, and then rethrow
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;
}
}