我知道当某个东西是 thrown 时,堆栈被“解除缠绕”到它被捕获的地方,并且在每个函数上下文中运行堆栈上类实例的析构函数(这就是为什么你不应该从析构函数中抛出一个异常——你可能最终抛出第二个异常) ... ... 但是我想知道在这种情况发生时,我抛出的对象在内存中存储在哪里?
throw
它是否依赖于实现? 如果是,是否有最流行的编译器使用的特定方法?
我不知道这是否能回答你的问题,但是 This (C + + 编译器如何实现异常处理)是一篇关于异常处理的优秀文章: 。我强烈推荐(:
很抱歉我的回答很简短,但是这篇文章的整个信息都很棒,我不能在这里挑选和发布一些信息。
是的,答案取决于编译器。
对我的编译器(g++ 4.4.3)进行了一个快速的实验,发现它的运行时库首先尝试使用 malloc内存来处理异常,如果这个尝试失败,就会尝试在数据段上的进程范围的“紧急缓冲区”中分配空间。如果不行,它就调用 std::terminate()。
g++ 4.4.3
malloc
std::terminate()
看起来,紧急缓冲区的主要用途是在进程耗尽堆空间后抛出 std::bad_alloc(在这种情况下,malloc调用将失败)。
std::bad_alloc
相关功能是 __cxa_allocate_exception:
__cxa_allocate_exception
extern "C" void * __cxxabiv1::__cxa_allocate_exception(std::size_t thrown_size) throw() { void *ret; thrown_size += sizeof (__cxa_refcounted_exception); ret = malloc (thrown_size); if (! ret) { __gnu_cxx::__scoped_lock sentry(emergency_mutex); bitmask_type used = emergency_used; unsigned int which = 0; if (thrown_size > EMERGENCY_OBJ_SIZE) goto failed; while (used & 1) { used >>= 1; if (++which >= EMERGENCY_OBJ_COUNT) goto failed; } emergency_used |= (bitmask_type)1 << which; ret = &emergency_buffer[which][0]; failed:; if (!ret) std::terminate (); } // We have an uncaught exception as soon as we allocate memory. This // yields uncaught_exception() true during the copy-constructor that // initializes the exception object. See Issue 475. __cxa_eh_globals *globals = __cxa_get_globals (); globals->uncaughtExceptions += 1; memset (ret, 0, sizeof (__cxa_refcounted_exception)); return (void *)((char *)ret + sizeof (__cxa_refcounted_exception)); }
我不知道这个计划有多典型。
来自 这一页:
异常需要存储 此存储必须持久存储 而堆栈正在解开,因为它 将由处理程序使用,并且必须 是线程安全的。 < strong > Exception 对象 因此,在一般情况下,储存 在堆 中分配,尽管 实施可能会提供一个 紧急缓冲区 强 > 支持投掷 低内存下 bad _ alloc 异常 条件。
现在,这只是安腾 ABI 和我正在寻找具体的细节,海湾合作委员会,Clang 和 MSVC。但是,该标准并没有指定任何内容,而且这似乎是实现异常存储的明显方法,所以..。
C + + 标准通常指定语言的行为方式,而不是编译器应该如何实现该行为。我认为这个问题属于那一类。实现这种功能的最佳方法取决于机器的具体情况——一些处理器有许多通用寄存器,而另一些只有很少的寄存器。处理器甚至可以只为异常构建一个特殊的寄存器,在这种情况下,编译器应该可以自由地利用这个特性。
它不能在栈上,因为它将被展开,而且它不能在堆上,因为这意味着系统可能不会抛出 std::bad_alloc。除此之外,它完全取决于实现: 没有指定实现(必须记录在案) ,但是没有指定。(一个实现可以在大多数情况下使用堆,只要它有某种紧急备份,即使在没有更多内存的情况下也允许有限数量的异常。)