别从析构函数往外抛异常

  《C++编程规范》第51条:析构(destructor)、释放(deallocation)和交换(swap)永不失败。

 

  我今天中招了。

 

  今天为了做实验而写了个小程序。我在程序中对每个系统调用都进行了错误检查,一旦发现调用失败,就抛出自定义的异常;而main函数的最后会捕捉所有异常,然后向终端输出友好的错误信息。

 

  谁知程序运行了四五个小时后,弹出了一个对话框,说:“XXX程序已经停止工作,Windows正在检查该问题的解决方案...” 然后问我要调试程序还是要取消。终端则根本没有输出任何我事先准备的错误消息。

 

  奇怪得很。这一般是内存访问异常才会出现的现象,而我的程序根本没有任何的数组或指针操作。

 

  折腾了很久,我还真点击了对话框里的 “调试” 按钮,开着Visual Studio对着一大堆的汇编代码调试。幸运的是,虽然程序被构建成Release版,但点击 “调试” 后打开的Visual Studio中还是能看见原来的C++代码和运行时栈。最后发现是这么回事:程序运行过程中,某个系统调用失败了。我的代码检查到了这个失败,也抛出了我自定义的异常。谁知程序在抛出异常并进行栈回退(stack unwinding)的时候,堆栈里的一个对象在析构时也抛出了异常。那个对象所具现的类是我自己定义的,其析构函数也会调用一个系统函数——当然,我对这个系统调用也进行了检查,在其出错时同样会抛出我自定义的类。于是,根据C++标准15.5.1节:

 

  If a destructor called during stack unwinding exits with an exception,std::terminate is called.

 

  翻译:如果在栈回退期间有某个析构函数抛出了异常,那么程序就会调用std::terminate。

 

  调用std::terminate的效果是啥?上文已经说过了。

 

  这下子得再跑上几小时的程序才能知道是哪个系统调用出错了。

 

  这一幕真是很好地诠释了什么叫 “不听老人言,吃亏在眼前。”

你可能感兴趣的:(编程,exception,windows,汇编,终端,destructor)