当抛出已有异常时构造函数抛出新异常,为何未调用std::terminate()?
解答
首先明确C++标准中std::terminate()的触发条件:只有当同时存在两个处于活跃状态的未捕获异常时,才会调用该函数终止程序。最常见的触发场景就是异常栈展开阶段(已抛出一个异常,正在向上查找catch块、销毁栈上局部对象的阶段),又有新的异常从栈展开调用的析构函数中抛出且未被处理。
你提到的「抛出异常的过程中,异常对象的构造/拷贝/移动函数抛出新异常」的场景,根本不会产生两个同时活跃的异常,自然不会触发终止,核心逻辑如下:
- 抛出异常的第一步是构造要抛出的异常对象,这个操作发生在栈展开启动之前。如果构造异常对象的过程中抛出了新异常,那么原本要抛出的旧异常根本没有完成初始化、也没有进入抛出流程,全程只有新抛出的这一个异常处于活跃状态,异常处理机制会正常处理这个新异常。
举个可运行的验证示例:
#include <iostream> #include <stdexcept> struct FaultyException { FaultyException() { // 构造自身时抛出另一个异常 throw std::runtime_error("FaultyException构造失败"); } }; int main() { try { throw FaultyException(); } catch (const std::runtime_error& e) { std::cout << "成功捕获异常: " << e.what() << std::endl; } return 0; }
上述代码可以正常运行并捕获到构造阶段抛出的异常,不会触发std::terminate()。
容易混淆的终止场景对比
只有当栈展开已经启动后再抛新异常才会触发终止,示例如下:
#include <iostream> #include <stdexcept> struct BadFoo { ~BadFoo() { // 栈展开阶段调用析构时抛新异常 throw std::runtime_error("析构函数抛出异常"); } }; int main() { try { BadFoo f; throw std::runtime_error("第一个异常"); // 抛出后进入栈展开,调用f的析构触发第二个异常 } catch (const std::runtime_error& e) { // 永远无法进入该分支,程序会直接终止 std::cout << e.what() << std::endl; } return 0; }
这个场景下确实存在两个同时活跃的异常,符合std::terminate()的触发条件。
内容的提问来源于stack exchange,提问作者user16756759
相关产品推荐
相关产品推荐

