当我"扔"某事时,它存储在内存中的什么位置?

sje*_*397 82 c++ exception-handling exception

我知道当某些东西是thrown时,堆栈被"解开"到它被捕获的点,并且每个函数上下文中的堆栈上的类实例的析构函数都被运行(这就是为什么你不应该从析构函数中抛出异常的原因) - 你可能最终会抛出第二个)...但是我想知道在内存中我扔的对象是在这种情况下存储的?

它是依赖于实现的吗?如果是这样,大多数流行的编译器是否使用了特定的方法?

NPE*_*NPE 51

是的,答案是编译器相关的.

我的编译器(g++ 4.4.3)的快速实验表明,它的运行时库首先尝试malloc为异常设置内存,如果失败,则会尝试在数据段上的进程范围"紧急缓冲区"中分配空间.如果这不起作用,它会调用std::terminate().

似乎紧急缓冲区的主要目的是std::bad_alloc在进程耗尽堆空间之后能够抛出(在这种情况下,malloc调用将失败).

相关功能是__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));
}
Run Code Online (Sandbox Code Playgroud)

我不知道这个方案有多典型.


Lig*_*ica 20

这个页面:

抛出异常需要存储.这个存储必须在堆栈被解除时保持不变,因为它将由处理程序使用,并且必须是线程安全的.因此,异常对象存储通常将在堆中分配,尽管 实现可能提供紧急缓冲区以支持在低内存条件下抛出bad_alloc异常.

现在,这只是Itanium ABI,我正在寻找GCC,Clang和MSVC的具体细节.但是,标准没有指定任何内容,这似乎是实现异常存储的明显方法,所以...


Kir*_*rov 6

我不知道这是否能回答您的问题,但是这篇(C++编译器如何实现异常处理)是一篇关于异常处理的优秀文章:。我强烈推荐它(:

很抱歉回答简短,但文章中的全部信息都很棒,我无法在这里挑选和发布一些信息。

  • 该链接确实描述了如何在良好的实现中不这样做。我知道一些较旧的 VC++ 使用了类似的东西,但我怀疑您是否会在任何现代编译器中找到它。 (2认同)