C++ 对待异常处理有两个规则
- 如果在
try...catch
中有异常抛出,则在catch
执行前,会先将try
语句块对应的栈清空 - C++ 不允许在同一个
try...catch
中处理1个以上的异常,如果发生此种情况,程序就会崩溃
为便于理解,我们先来看一个例子
class Dog {
public:
string name;
Dog(string name) {this->name = name; cout << name << " is born.\n"; }
~Dog() { cout << name << " is destroied.\n"; }
void bark() { cout << name << " is barking.\n"; }
};
int main() {
try {
Dog dog1("Henry");
Dog dog2("Bob");
throw 20;
dog1.bark();
dog2.bark();
} catch (int e) {
cout << e << " is caught" << endl;
}
}
/*
* output:
* Henry is born.
* Bob is born.
* Bog is destroied.
* Henry is destroied.
* 20 is caught
*/
从上面的例子可以看出,catch
语句块在两个局部对象析构完成后才执行,意味着在异常被捕获之前,try
代码块中的栈需要被清理。
我们把上面代码稍作修改,把 throw
语句放到析构函数中,看下会发生什么
class Dog {
public:
string name;
Dog(string name) {this->name = name; cout << name << " is born.\n"; }
~Dog() { cout << name << " is destroied.\n"; throw 20;}
void bark() { cout << name << " is barking.\n"; }
};
int main() {
try {
Dog dog1("Henry");
Dog dog2("Bob");
dog1.bark();
dog2.bark();
} catch (int e) {
cout << e << " is caught" << endl;
}
}
/* Output:
* Henry is born.
* Bob is born.
* Henry is barking.
* Bob is barking.
* Bob is destroied.
* Henry is destroied.
* libc++abi.dylib: terminating with uncaught exception of type int
* [1] 51549 abort ./exception
*/
可以看到程序崩溃了,崩溃原因是 terminating with uncaught exception of type int
:异常没有被处理。我们来分析下其中的原因,在 try
中,我们定义了两个对象,并按照顺序调用了它们的 bark
接口,随后离开 try
代码块,此时,编译器会自动释放这两个局部对象,调用它们的析构函数,因为栈的特性是后进先出,所以先析构 Bob
,在执行 Bob
的析构函数时,抛出了异常,但此时并不会立即执行 catch
语句块,根据上文提到的第一条规则:「在 catch
执行前,需要先清理 try
中的堆栈」。于是 Henry
也被析构了,这让 try
语句块中抛出了 2 个异常,直接导致了程序的崩溃。
找到上述程序崩溃的元凶后,我们便学到了一条宝贵的 C++ 经验:
不要在析构函数中抛出异常。
因为如果你的析构函数中有异常抛出的话,你便无法控制 try
语句中抛出来的异常数量——这将是一场灾难。
为了不在析构函数中抛出异常,一般有两种做法:
-
在析构函数内部捕获异常,防止异常被抛出,例如下面的代码
~Dog { try { // may throw exception } catch (MyException e) { // Catch exception } catch (...) { // 尽量不要使用 ... 来捕获异常 } }
虽然这样做,你的析构函数再不会抛出异常了,但却带来了一些隐患,即你使用了
(...)
来捕获异常,这种代码没有任何用处(它无法输出有效的异常信息),同时由于它会捕获一切异常,于是会将一些必要的程序缺陷掩盖起来,而不是“尽早的暴露问题”。所以在这里,我们学到的第二条经验是:不要使用
(...)
来捕获异常 保持析构函数简洁,将可能导致异常的代码移到其他的函数中。这才是推荐的做法。
参考