通过引用捕获异常危险吗?

Som*_*ing 63 c++ exception

请查看以下异常抛出和捕获:

void some_function() {
    throw std::exception("some error message");
}

int main(int argc, char **argv) {
    try {
        some_function();
    } catch (const std::exception& e) {
        std::cerr << e.what() << std::endl;
        exit(1);
    }
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

通过引用捕获抛出的异常是否安全?

我担心的是因为异常e实际上是放在堆栈上some_function().但some_function()刚刚回来,导致e被破坏.所以实际上现在e指向一个被破坏的对象.

我的担忧是否正确?

传递异常而不按值复制它的正确方法是什么?我应该抛出new std::exception()它放在动态内存中吗?

Ton*_*roy 93

确实安全 - 并且建议 - 通过const引用来捕获.

" e实际上是放在堆栈上some_function()"

不,它不是......实际抛出的对象是在一个未指定的内存区域中创建的,该区域保留供异常处理机制使用:

[except.throw] 15.1/4:异常对象的内存以未指定的方式分配,除非在3.7.4.1中说明. 异常对象要么最后剩余的活性为异常退出比重新抛出其他任何手段,或者类型为std的最后一个对象:: exception_ptr(18.8.5),该指异常对象被销毁处理程序后毁坏,取后.

如果指定了局部变量throw,则在必要时将其复制到那里(优化器可能能够在其他内存中直接创建它).这就是为什么...

15.1/5 当抛出的对象是类对象时,即使复制/移动操作被省略,也应该可以访问为复制初始化和析构函数选择的构造函数(12.8).


如果没有点击,可能有助于想象实现模糊地这样:

// implementation support variable...
thread__local alignas(alignof(std::max_align_t))
    char __exception_object[EXCEPTION_OBJECT_BUFFER_SIZE];

void some_function() {
    // throw std::exception("some error message");

    // IMPLEMENTATION PSEUDO-CODE:
    auto&& thrown = std::exception("some error message");
    // copy-initialise __exception_object...
    new (&__exception_object) decltype(thrown){ thrown };
    throw __type_of(thrown);
    // as stack unwinds, _type_of value in register or another
    // thread_local var...
}

int main(int argc, char **argv)
{
    try {
        some_function();
    } // IMPLEMENTATION:
      // if thrown __type_of for std::exception or derived...
      catch (const std::exception& e) {
        // IMPLEMENTATION:
        // e references *(std::exception*)(&__exception_object[0]);
        ...
    }
}
Run Code Online (Sandbox Code Playgroud)


小智 22

必须通过引用捕获,否则您无法获得对象的正确动态类型.至于它的寿命,标准保证,在[except.throw],

异常对象要么最后剩余的活性为异常退出比重新抛出其他任何手段,或者类型为std的最后一个对象:: exception_ptr(18.8.5),该指异常对象被销毁处理程序后毁坏,取后

  • 好吧,你没有*必须*,这样做是合法的.但是,如果你不这样做,那么你已经瞄准了自己的膝盖.如果按值捕获,则引入可能的中间副本,更重要的是引入对象切片. (4认同)

Ric*_*ges 18

通过const引用捕获正是应该如何捕获异常.异常对象不一定存在于"堆栈"中.编译器负责使这项工作适当的魔术.

另一方面,您的示例无法编译,因为std::exception可能只是默认构造或复制构造.在这种情况下,该what()方法将返回指向空(c样式)字符串的指针,这不是特别有用.

建议你扔一个std::runtime_errorstd::logic_error适当的,或从中派生的类:

  • logic_error 当调用者请求了服务的设计参数之外的内容时.
  • runtime_error 当呼叫者请求合理的内容但外部因素阻止您尊重请求时.

http://en.cppreference.com/w/cpp/error/exception


Sig*_*ndo 8

来自except.throw:

抛出异常copy-initializes(8.5,12.8)一个临时对象,称为异常对象.临时是一个左值,用于初始化匹配处理程序中声明的变量(15.3).如果异常对象的类型是不完整类型或指向不完整类型的指针(可能是cv-qualified),则程序格式不正确.

这是抛出异常的行为,该异常将异常对象复制到任何堆栈之外的异常区域中.因此,通过引用捕获异常是完全合法的,也是可取的,因为异常对象的生存期将延长到最后一次catch().