在堆上分配异常的任何陷阱?

Tri*_*ock 4 c++ exception-handling exception

问题说明了一切:在堆上分配异常是否有任何陷阱?

我问,因为在堆上分配异常,结合多态异常习惯用法,解决了在线程之间传输异常的问题(为了讨论,假设我不能使用exception_ptr).或者至少我认为它确实......

我的一些想法:

  • 异常的处理程序必须捕获异常并知道如何删除它.这可以通过实际抛出auto_ptr适当的删除器来解决.
  • 是否有其他方法跨线程传输异常?

Max*_*kin 5

在堆上分配异常是否有任何陷阱?

一个明显的缺陷是堆分配可能会失败.

有趣的是,当抛出异常时,它实际上抛出了作为参数的异常对象的副本throw.使用gcc时,它会在堆中创建该副本,但会产生扭曲.如果堆分配失败,它使用静态紧急缓冲区而不是堆:

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 ();
    }
}
Run Code Online (Sandbox Code Playgroud)

因此,一种可能性是复制此功能以防止异常的堆分配失败.

异常的处理程序必须捕获异常并知道如何删除它.这可以通过使用适当的删除器实际抛出auto_ptr来解决.

不确定使用auto_ptr<>是否是一个好主意.这是因为复制auto_ptr<>会破坏原始文件,因此在捕获值catch(std::auto_ptr<std::exception> e)throw;,如果没有参数重新抛出原始异常,可能会抛出一个NULL auto_ptr<>因为它被复制而来(我没试过).

我可能会因为这个原因抛出一个普通的指针,比如throw new my_exception(...)按值捕获并手动捕获delete它.因为手动内存管理留下了泄漏内存的方法,我会创建一个小型库,用于在线程之间传输异常并在其中放置这样的低级代码,以便其余代码不必关心内存管理问题.

另一个问题是需要一个特殊的throw语法,例如throw new exception(...),可能有点过于干扰,也就是说,可能存在无法更改的现有代码或第三方库,这些库以标准方式抛出throw exception(...).坚持标准throw语法并捕获所有可能的异常类型(必须事先知道,并作为后备只是切片异常并仅复制基类子对象)可能是一个好主意.顶级线程catch块,复制该异常并在另一个线程中重新抛出副本(可能在join提取另一个线程结果的函数上或其中,尽管抛出的线程可能是独立的并且根本不会产生结果,但这是一个完全另一个问题,我们假设我们处理某种有限生命的工作线程).这样,另一个线程中的异常处理程序可以通过引用或值以标准方式捕获异常,而无需处理堆.我可能会选择这条路.

您还可以查看Boost:在线程之间传输异常.