如果异常类的构造函数抛出异常,会发生什么?
1. 如果异常类的复制构造函数抛出异常,则程序会中止(abort)。
上述结论是肯定成立的,但是对此还有很多实际情况需要区分对待,如下所述。
首先:
class foo{
public:
foo() {}
foo(const foo&){
throw 1;
}
};
int main(int argc, char **argv){
try{
foo f;
throw f;
}
catch(...){
cout << "Ok, reach here" << endl;
}
return 0;
}
在GCC3.4.2上运行程序,结果:
This application has requested the Runtime to terminate it in an unusual way.
Please contact the application's support team for more information.
这表明,在throw f的同时,对异常对象f进行拷贝时抛出了异常,程序被非正常中止了。
(但是,在vs2005上的实验结果是:正常显示“Ok, reach here”,并没有中止程序:(,不解其缘由)
对上述程序稍作修改,如下:
class foo{
public:
foo() {}
foo(const foo&){
throw 1;
}
};
int main(int argc, char **argv){
try{
throw foo();//此处抛出一个临时对象,而非命名对象
}
catch(...){
cout << "Ok, reach here" << endl;
}
return 0;
}
在GCC3.4.2上运行程序,结果:
Ok, reach here
此处程序并没有被非正常中止,因为throw foo()抛出的是一个临时对象,理论上的过程是这样的:“先调用foo()构造一个临时对象,然后将此临时对象复制一份作为抛出的异常进行传播”,但是编译器对此过程作了优化,将拷贝的过程优化掉了(比如直接将这个临时对象进行传播,或者直接在要抛出的异常对象中进行foo()的调用对其初始化进而而不产生临时对象,等等),所以在throw foo()的时候没有拷贝的行为发生。此外,这个程序特殊的地方还在于,不仅在产生异常处(即throw处)没有拷贝行为,而且在异常的接收端(即catch处)也同样没有拷贝行为,因为catch(...)表示接收任何异常,用户无法在catch中引用到这个被捕获的异常,所以没有拷贝行为(注:如果按引用捕获异常catch(foo &f),也同样没有拷贝行为)。
(在vs2005上的实验结果也是:正常显示“Ok, reach here”,与GCC相同)
对程序再作修改,如下:
class foo{
public:
foo() {}
foo(const foo&){
throw 1;
}
};
int main(int argc, char **argv){
try{
throw foo();
}
catch(foo){ //此处发生了改变
cout << "Ok, reach here" << endl;
}
return 0;
}
在GCC3.4.2上运行程序,结果:
This application has requested the Runtime to terminate it in an unusual way.
Please contact the application's support team for more information.
这表明在异常捕获端(即catch(foo)处)发生了拷贝行为(尽管在异常抛出处(即throw处)没有发生拷贝行为,如上面所述),所以程序非正常中止。
(但是,在vs2005上的实验结果是:正常显示“Ok, reach here”,并没有中止程序。如果将catch(foo)改成catch(foo f),则结果就与GCC相同了(程序非正常中止)。这是因为vs看到catch(foo)并没有给出异常的名称,因此在catch中仍然无法引用到这个被捕获的异常,所以就没有进行拷贝。可是我认为这种做法不太妥当,应该是一种“不成熟的优化”,因为它没有考虑到copy constructor有副作用的情况,即如果copy constructor除了拷贝之外还担负着其他的责任(当然,这不符合“单一职责原则”,但现实中确实可能存在),那vs的这种优化就会让程序的行为出乎编程者的意料。)
2. 如果异常类的其他构造函数抛出异常,则在此异常被抛出之前(也就是说,此异常并没有被抛出)另外一个异常就产生了,所以程序并不会中止,而是按照正常的方法去捕获并处理这个被抛出的异常。
例子:
class foo{
public:
foo(){
throw 1;
}
foo(const foo&){}
};
int main(int argc, char **argv){
try{
throw foo();
}
catch(...){
cout << "Ok, reach here" << endl;
}
return 0;
}
在GCC3.4.2上运行程序,结果:
Ok, reach here
我们可以这样想这个程序:
int main(int argc, char **argv){
try{
foo temp;
throw temp;
}
catch(...){
cout << "Ok, reach here" << endl;
}
return 0;
}
因此,在“foo temp;”处就已经抛出异常了,所以根本就没有执行到throw,因此这个foo类型的异常根本就没有被抛出过,而catch(...)捕获到的异常是int型的1(foo的default constructor中throw 1的结果)。
(在vs2005上的实验结果也是:显示“Ok, reach here”,与GCC相同)