为什么被捕后我的异常仍被抛出?

时间:2013-12-03 01:49:41

标签: c++ c++11 exception-handling

我有以下代码,其中使用函数调用的结果初始化变量。这个函数抛出所以我设置了一个try-catch来捕获异常。由于某种原因,即使在catch子句运行之后,异常仍然出现在屏幕上。

#include <iostream>
#include <stdexcept>

int f() { throw std::invalid_argument("threw"); return 50; }

struct S
{
    S()
        try : r(f())
    {
        std::cout << "works";
    }
    catch(const std::invalid_argument&)
    {
        std::cout << "fails";
    }

    int r;
};

int main()
{
    S s;
}

此代码在显示异常后打印“失败”:

terminate called after throwing an instance of 'std::invalid_argument'
what():  threw

为什么异常仍然被抛出?我在main中设置了相同的代码,它可以正常工作:

int main()
{
    try { throw std::invalid_argument("blah"); }
    catch(const std::invalid_argument&) { }
}

那么为什么在初始化列表中使用它会失败?

2 个答案:

答案 0 :(得分:14)

具有函数try块的构造函数(就像你对S所拥有的那样)会自动重新抛出catch块捕获的任何异常。因此,在catch捕获异常后,它会重新抛出异常。此行为与普通catch处理程序不同,后者不执行此操作。我认为基本原理是,如果构造数据成员或基类失败,则该对象无法构造。 catch处理程序的目的只是在异常向外传播之前进行任何额外的清理。

希望这有帮助!

答案 1 :(得分:2)

来自C ++ 11 Standard,15.3 / 15:

  

如果控制到达function-try-block的处理程序的末尾,则重新抛出当前处理的异常   构造函数或析构函数。

原因在你的问题下的GOTW Jerry链接中得到了很好的解释,但是简单地说:想象一下如果它没有被重新生成,那么S s;之后的下一行可能会尝试使用s尽管它从来没有完成构造,当s离开作用域时,构造函数将安排对s的析构函数的调用 - 可能释放从未初始化的指针,释放永不占用的锁等。

相比之下,如果您让数据成员默认初始化,然后从构造函数体中的try / catch块分配给它,则包含基础和数据成员的对象的状态可能会保持在某种连贯状态:作为程序员决定该状态是否正常 - 取决于你是否在构造函数体内使用try / catch块,以及后面的成员函数处理可能的默认构造数据成员。这取决于你。