当抛出 C++ 异常并发生堆栈展开时,需要有一些存储区域让异常对象保持活动状态,直到执行后离开最外层 catch 块的范围。
但是这个存储空间究竟在哪里呢?在stackoverflow上寻找答案,我发现:https : //stackoverflow.com/a/27259902/2923952
这个答案引用了标准,说:
异常对象的内存以未指定的方式分配,除非在 3.7.4.1 中注明。
... [注意:特别是,不会调用全局分配函数来为 [...] 异常对象 (15.1) 分配存储空间。— 尾注]
那讲得通。您不希望编译器生成对幕后malloc或new幕后的调用,因为您可能正在编写具有特定Allocator要求的代码。
由于活动异常的数量受硬编码堆栈深度的限制,因此似乎没有任何需要为异常动态分配空间。编译器可能只有一些堆栈空间或每个线程的静态存储来放置活动异常。
但现在我们有std::exception_ptr. 这基本上是一个活动异常对象的 shared_ptr,只要该异常对象的任何实例std::exception_ptr仍然存在,该异常对象就会保持活动状态。
但由此推论,我们现在可以无限期地延长任何活动异常的生命周期。所以基本上我可以有一个std::vector<std::exception_ptr>, 然后在一个循环中我可以不断抛出异常并将每个指向当前异常的指针存储在我的向量中。所以我可以通过这种方式动态生成数百万个活动异常。
std::vector<std::exception_ptr> active_exceptions;
for (;;)
{
try { throw int{}; }
catch (...) { active_exceptions.push_back(std::current_exception()); }
}
Run Code Online (Sandbox Code Playgroud)
这将迫使编译器以某种方式动态添加更多存储以保持这些异常活动。那么它是如何做到这一点的呢?它是否会在静态存储用完后重新使用malloc/ new?
分配异常存储的方式是实现定义的。在这方面的实现差异很大。当您抛出/捕获异常时,MSVC 使用堆栈空间来存储异常(在展开期间有效地缩短堆栈)。其他实现实际上为异常动态分配内存(异常本身可能会因异常而失败。这很有趣)。
但是,不为 throw/catch 行为动态分配内存的实现几乎总是在您使用current_exception获取exception_ptr异常时执行动态分配。这个过程的语义本质上要求异常对象存在于动态分配中,您可以在函数的几个方面看到这一点。
例如,current_exception据说返回异常对象的引用或其副本;发生哪一种情况是由实现定义的。这也是为什么current_exception它本身可以伪抛出std::bad_alloc。也就是说,如果期间内存分配current_exception失败,则不会将当前异常放入 中exception_ptr,而是std::bad_alloc将其推入其中。
| 归档时间: |
|
| 查看次数: |
72 次 |
| 最近记录: |