如何在堆栈上分配的异常超出了它们的范围?

Joh*_*Doe 23 c++ callstack exception-handling exception

在下面的代码中,基于堆栈的变量"ex"被抛出并被捕获到超出声明ex的范围的函数中.这对我来说有点奇怪,因为(AFAIK)基于堆栈的变量不能在它们被声明的范围之外使用(堆栈被展开).

void f() {
    SomeKindOfException ex(...);
    throw ex;
}

void g() {
    try {
        f();
    } catch (SomeKindOfException& ex) {
        //Handling code...
    }
}
Run Code Online (Sandbox Code Playgroud)

我已经向SomeKindOfException的析构函数添加了一个print语句,它显示ex在f()中超出范围后被破坏但是它在g()中被捕获并且一旦它超出范围就再次被破坏.

有帮助吗?

And*_*bel 20

异常对象被复制到一个特殊位置,以便在堆栈展开后继续存在.您看到两个析构的原因是因为当您退出f()时,原始异常将被销毁,当您退出g()时,副本将被销毁.


Joh*_*itb 10

该对象被复制到一个异常对象中,该对象在堆栈展开后仍然存在.该对象的内存来自何处未指定.对于大对象,它可能是malloc'ed,对于较小的对象,实现可能有一个预分配的缓冲区(我可以想象这可以用于bad_alloc异常).

ex然后将引用绑定到该异常对象,该对象是临时的(它没有名称).


Kir*_*sky 10

C++标准15.1/4:

除非在3.7.3.1中指出,否则将以未指定的方式分配要抛出的异常的临时副本的内存.只要存在针对该异常执行的处理程序,临时就会持续存在.特别是,如果处理程序通过执行throw而退出; 语句,将控制权传递给另一个处理程序以获得相同的异常,因此临时保留.当为异常执行的最后一个处理程序以throw以外的任何方式退出时; 临时对象被销毁,实现可能会释放临时对象的内存; 任何这种解除分配都是以未指明的方式完成的.在销毁处理程序中的exception-declaration中声明的对象后立即发生破坏.

没有什么可说的了.


Mat*_*lia 6

当您抛出ex时,它将被复制到用于抛出异常对象的特殊内存位置.这种拷贝由普通拷贝构造器执行.

您可以通过此示例轻松查看此内容:

#include <iostream>

void ThrowIt();

class TestException
{
  public:
    TestException()
    {
        std::cerr<<this<<" - inside default constructor"<<std::endl;
    }

    TestException(const TestException & Right)
    {
        (void)Right;
        std::cerr<<this<<" - inside copy constructor"<<std::endl;
    }

    ~TestException()
    {
        std::cerr<<this<<" - inside destructor"<<std::endl;    
    }
};

int main()
{
    try
    {
        ThrowIt();
    }
    catch(TestException & ex)
    {
        std::cout<<"Caught exception ("<<&ex<<")"<<std::endl;
    }
    return 0;
}

void ThrowIt()
{
    TestException ex;
    throw ex;
}
Run Code Online (Sandbox Code Playgroud)

样本输出:

matteo@teolapubuntu:~/cpp/test$ g++ -O3 -Wall -Wextra -ansi -pedantic ExceptionStack.cpp -o ExceptionStack.x
matteo@teolapubuntu:~/cpp/test$ ./ExceptionStack.x 
0xbf8e202f - inside default constructor
0x9ec0068 - inside copy constructor
0xbf8e202f - inside destructor
Caught exception (0x9ec0068)
0x9ec0068 - inside destructor
Run Code Online (Sandbox Code Playgroud)

顺便说一句,你可以在这里看到用于抛出对象的内存位置(0x09ec0068)肯定远离原始对象(0xbf8e202f):堆栈像往常一样具有高地址,而内存用于抛出的对象在虚拟地址空间中完全失效.尽管如此,这是一个实现细节,因为正如其他答案所指出的那样,该标准没有说明抛出对象的内存应该在何处以及应该如何分配.