C++中抛出异常的生命周期

Lifecycle of thrown exceptions in C++

本文关键字:生命 周期 抛出异常 C++      更新时间:2023-10-16

考虑以下简单的C++代码:

void foo() {
    throw(my_exception());
}
void check_exc(const my_exception &exc) {
    /* Do stuff with exc */
}
void bar() {
    try {
        foo();
    } catch(const my_exception &exc) {
        check_exc(exc);
    }
}

bar的异常处理程序中,为什么exc引用的异常仍然存在,就像它在foo的堆栈帧中是如何分配的一样?在异常处理程序运行时,该帧不应该已经展开,并且在那里分配的任何值都已经被认为是死的吗?尤其是因为我显式地调用了另一个需要堆栈空间的函数。

作为一个试图学习C++的C程序员,我在这里误解了什么?更确切地说,这些不同的值在内存中实际存在于哪里?

在throw表达式中创建的临时用于初始化异常对象本身,(引用标准)"以未指定的方式分配"。该对象(至少)会持续到处理完异常,因此处理程序对它的引用在处理程序或从处理程序调用的任何函数中都是有效的。

异常由值抛出。

也就是说,指定的对象被复制(可能是切片的,就像任何复制初始化一样)或移动。

可以通过例如catch子句的引用参数引用的异常对象不是在原始抛出代码的堆栈帧中分配的,而是在“以未指明的方式";。


在C++11中,异常对象的可能分配方式(在运行时库内部)受到限制,因为要求异常对象可以由本质上共享的所有权智能指针std::exception_ptr引用。

共享所有权的可能性意味着在C++中,异常对象可以在完成异常处理之后有一个有保证的生存期。

这主要是为了支持通过非异常感知的C代码传递异常,并传递嵌套的异常信息。

实现因平台而异。由于throw语句开始在foo()的堆栈帧中执行,因此生命周期比想象的要复杂。

异常对象可能会获得副本并重新分配,或者catch块可能会在foo顶部的一个帧中执行,但会使用指向bar帧的指针来引用bar的变量。

He,

线路

 throw(my_exception()) 

生成类型为my_exception的新对象。您可以指定所需的任何内容(int、enums、char*或类)。当然,类更有意义,因为你可以为它定义额外的数据

之后,整个堆栈将被清理,所有递归都将终止,直到它到达第一个try/catch块。例外仍然存在。catch块中的代码就像实现if/else的块一样,只是稍微智能一点。

欢呼,