带抛出析构函数的对象能否逃逸`...`捕获器?示例程序是否存在未定义行为?
带抛出析构函数的对象能否逃逸
...捕获器?示例程序是否存在未定义行为? 这个问题拆解开来其实包含两个关键点:为什么示例里的异常能逃出内层的catch(...),以及这段代码有没有未定义行为,咱们一步步理清楚。
首先看代码的执行流程:
- 内层
try块里抛出了throwing_dtor_t类型的临时对象,内层的catch(...)成功捕获到这个异常,进入catch块执行(这里catch块是空的,啥也没做)。 - 当内层
catch块执行完毕后,需要销毁异常对象(就是那个被抛出的throwing_dtor_t实例,这是C++异常机制自动创建的副本),而这个对象的析构函数抛出了int类型的123。 - 这个新抛出的异常,已经不在内层
catch(...)的处理范围内了——因为内层的异常处理流程已经走到了收尾的清理步骤,程序的执行上下文回到了外层的try块中,所以这个新异常会向上传播,被外层的catch(int)捕获,最终程序返回123。
所以第一个问题的答案就很明确了:不是初始的异常逃出了catch(...),而是在第一个异常的处理收尾阶段(销毁异常对象)抛出了新的异常,这个新异常是完全独立的,自然不受已经执行完毕的内层catch(...)管辖,也就会“逃逸”到外层的异常处理逻辑中。
接下来是未定义行为的问题:这段代码不存在未定义行为。
- 首先,
throwing_dtor_t的析构函数明确标记了noexcept(false),这是合法的——C++标准允许析构函数抛出异常,只要显式声明允许(默认情况下析构函数是noexcept(true),如果没声明的话抛出异常会触发std::terminate,但这里已经用noexcept(false)放开了限制)。 - 其次,在清理异常对象时抛出的新异常,只要能被外层的异常处理程序捕获,程序就会按照正常的异常传播逻辑执行,标准里并没有把这种情况定义为未定义行为。只有当在栈展开(处理异常的过程中销毁自动对象)时抛出新异常,且这个新异常没有被任何
catch捕获的情况下,才会触发std::terminate,而你的代码中外层的catch(int)成功捕获了新异常,所以完全符合标准要求。
内容来源于stack exchange
相关产品推荐
相关产品推荐

