26

这是超级基本的,但我无法在任何地方找到答案。那里有很多关于投掷和接球的帖子,但是如果我throwfunction1然后调用但没有接住它,实际上会发生什么,这是否意味着它只是被重新抛出给调用function1者?从以下内容来看,我会说是的,但我想在我坚持下去并假设之前得到一个可靠的大师般的答案:function2function2

#include <iostream>

void function1()
{
    throw 1;
}

void function2()
{
    function1();
}

int main()
{
    try
    {
        function2();
    }
    catch(...)
    {
        std::cout << "caught!";
        return 0;
    }
    return 0;
}

Output: caught!

4

6 回答 6

22

是的,这就是异常的工作方式。当异常被抛出时,它会被调用堆栈中的最顶层函数捕获,该函数在执行范围内具有该异常的处理程序。由于您要返回堆栈中较低的函数,因此较高堆栈帧中函数范围内的某些变量需要超出范围,因此会调用它们的析构函数。这称为堆栈展开。将它与 RAII 结合起来真是太好了(如果您不知道那是什么,请查找 RAII)。但是,如果任何析构函数在堆栈展开期间抛出异常,那么它是坏的并且该std::terminate函数将被调用。通常,您的程序将结束(这就是为什么总是建议您编写非抛出的析构函数)。

来自cppreference.com

一旦构造了异常对象,控制流就会向后工作(向上调用堆栈),直到到达 try 块的开头,此时将关联的 catch 块的参数与抛出的表达式进行比较以找到匹配项。如果未找到匹配项,则控制流继续展开堆栈,直到下一个 try 块,依此类推。如果找到匹配项,则控制流跳转到匹配的 catch 块(异常处理程序),该块正常执行。

随着控制流在调用堆栈中向上移动,自进入相应的 try 块以来,所有具有自动存储持续时间的对象都会以相反的构造顺序调用析构函数。如果构造函数抛出异常,则为所有完全构造的非静态非变体成员和基类调用析构函数。这个过程称为堆栈展开。

于 2013-10-17T14:26:04.520 回答
11

由于function2()并且function1()不捕获异常,它将向上传播调用堆栈,直到它被您拥有的第一个合适的处理程序捕获main()。局部对象析构函数被称为堆栈展开。如果您没有合适的处理程序,C++ 运行时将调用unexpected()内置函数,该函数将调用abort()并终止程序。

于 2013-10-17T14:26:32.113 回答
7

是的,但它不会被“重新抛出”——简单地说,当你抛出一个异常时,它会遍历调用堆栈,直到找到一个catch可以处理它的块;这是异常最重要的“卖点”之一。

如果没有找到合适的处理程序,std::terminate则调用并且您的程序异常终止(请注意,在这种情况下,不能保证会调用析构函数)。

于 2013-10-17T14:26:37.100 回答
5

这是否意味着它只是被重新抛出给 function2 的调用者?

不,它不会被重新抛出;原件throw根据需要将其发送到调用堆栈的最上层,直到找到处理程序。function1在这种情况下, or中没有处理程序function2,因此它最终出现在 中的处理程序中main

如果它根本没有被抓住,并试图离开main,那么程序将终止。(有一些方法可以改变这种行为,但这与这个问题并不特别相关)。

于 2013-10-17T14:26:44.150 回答
4

考虑以下程序:

#include <iostream>

void function1()
{
    try
    {
        throw 1;
    }
    catch(...)
    {
        std::cout << "Exception caught in function1." << std::endl;
        throw 1;
    }
}

void function2()
{
    try
    {
        function1();
    }
    catch(...)
    {
        std::cout << "Exception caught in function2." << std::endl;
        throw 1;
    }
}

int main()
{
    try
    {
        function2();
    }
    catch(...)
    {
        std::cout << "Exception caught in main." << std::endl;
    }

    return 0;
}

它的输出是

Exception caught in function1.
Exception caught in function2.
Exception caught in main.
于 2013-10-17T14:29:50.973 回答
0

你可以在没有任何 try 和 catch 块的情况下抛出。例如,

std::string &someClass::operator[](unsigned position) {
   // try {
   if (position <= length && position >= 0) {
       //return string at that position;
   }else{
       throw ("out of range");
   }
}

在这种情况下,该函数检查位置是否在动态数组长度内。否则,它会抛出一个字符串,告诉用户他们选择的位置超出了界限。所以有一些方法可以在没有 try 和 catch 块的情况下使用 throw 语句(但是 try 和 catch 一起使用,不能排除一个)。

于 2020-02-06T16:21:01.373 回答