我理解当某个东西是throw
n时,堆栈被“解开”到它被捕获的位置,并且每个函数上下文中堆栈上的类实例的析构函数都会被运行(这就是为什么你不应该从析构函数中抛出异常 - 你最终可能会抛出第二个...)但是我想知道在内存中我抛出的对象在这种情况下会被存储?
是否依赖于实现?如果是这样,大多数流行的编译器是否使用了特定的方法?
答案 0 :(得分:51)
是的,答案是编译器相关的。
我的编译器(g++ 4.4.3
)的快速实验表明,它的运行时库首先尝试malloc
内存以查找异常,如果失败,则尝试在进程范围内的“紧急缓冲区”中分配空间“它存在于数据部分。如果不能解决问题,则会调用std::terminate()
。
紧急缓冲区的主要目的似乎是在进程耗尽堆空间后抛出std::bad_alloc
(在这种情况下malloc
调用将失败)。 / p>
相关功能是__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));
}
我不知道这个方案有多典型。
答案 1 :(得分:20)
来自this page:
存在异常需要存储 抛出。此存储必须保留 堆栈正在解开,因为它 将由处理程序使用,并且必须 是线程安全的。 异常对象 因此通常存储 尽管如此,在堆中分配 实现可能会提供 紧急缓冲区支持投掷 低内存下的bad_alloc异常 条件。
现在,这只是Itanium ABI,我正在寻找GCC,Clang和MSVC的具体细节。但是,标准没有指定任何内容,这似乎是实现异常存储的明显方法,所以......
答案 2 :(得分:4)
我不知道这是否会回答你的问题,但this (How a C++ compiler implements exception handling)是关于异常处理的优秀文章:。我强烈推荐它(:
很抱歉答案很简短,但文章中的所有信息都很棒,我不能在这里挑选和发布一些信息。
答案 3 :(得分:0)
C ++标准通常指定语言的行为方式,但不指定编译器应如何实现该行为。我认为这个问题属于这一类。实现这样的事情的最佳方式取决于机器的细节 - 一些处理器有很多通用寄存器,有些只有很少。甚至可以使用特殊寄存器构建处理器以用于异常,在这种情况下,编译器应该可以自由地利用该功能。
答案 4 :(得分:-2)
好吧,它不能在堆栈上,因为它将被解除,并且它不能在堆上,因为这意味着系统可能无法抛出std::bad_alloc
。除此之外,它完全取决于实现:未指定实现(必须记录),但未指定。 (实现可以在大多数时间使用堆,只要它具有某种紧急备份,即使没有更多内存也会允许有限数量的异常。)