我有以下代码,其中使用函数调用的结果初始化变量。这个函数抛出所以我设置了一个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&) { }
}
那么为什么在初始化列表中使用它会失败?
答案 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块,以及后面的成员函数处理可能的默认构造数据成员。这取决于你。