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

时间:2011-10-07 17:01:10

标签: c++ exception exception-handling

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

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

我的一些想法:

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

4 个答案:

答案 0 :(得分:5)

  

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

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

有趣的是,当抛出异常时,它实际上会抛出作为throw参数的异常对象的副本。使用gcc时,it creates that copy in the heap但有一个扭曲。如果堆分配失败,它将使用静态紧急缓冲区而不是堆:

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 ();
    }
}

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

  

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

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

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

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

您也可以查看Boost: Transporting of Exceptions Between Threads

答案 1 :(得分:3)

有两个明显的一个: 一个 - 容易 - 是throw new myexception冒险抛出bad_alloc(而非bad_alloc*),因此catch exception*无法捕获最终无法分配异常。抛出new(nothrow) myexception可能会抛出一个空指针。

另一个 - 更多的设计问题 - 是“谁必须抓住”。如果不是您自己,请考虑一种情况,即您的客户(也可能是其他人的客户) - 根据谁投掷 - 决定是否删除。可能会导致混乱。

解决问题的典型方法是通过引用(或地址)抛出静态变量:不需要删除,并且在展开的堆栈中向下时不需要复制

答案 2 :(得分:0)

如果您抛出异常的原因是堆存在问题(内存不足或其他情况),则在堆上分配异常只会导致更多问题。

答案 3 :(得分:0)

投掷std::auto_ptr<SomeException>时还有另一个问题,并且还会提升共享指针:虽然runtime_error来自exception,但auto_ptr<runtime_error>不是来自auto_ptr<exception>

因此,catch(auto_ptr<exception> &)无法捕获auto_ptr<runtime_error>