【问题标题】:Exception behavior C++14 vs C++98异常行为 C++14 与 C++98
【发布时间】:2017-04-18 12:53:17
【问题描述】:

我写了以下程序

#include <iostream>
#include <stdexcept>

class Myclass
{
    public:
    ~Myclass() 
    {
        //throw std::runtime_error("second (in destructor)");
        throw 1;
    }
};

void fun()
{
    Myclass obj;
}
int main()
{   
    try
    {
        fun();      
    }
    catch (const std::exception& e)
    {
       std::cout << e.what();
    }
    catch(...)
    {
       std::cout << " ... default Catch" << std::endl; 
    }
    std::cout << "Normal" << std::endl;
    return 0;
}  

当我以C++98 模式(cpp.sh)运行上面的程序时,它会打印

 ... default Catch
Normal

当我以C++14 模式运行它时,它不会打印任何内容。 为什么这种行为会发生变化?

我明白,每当发生任何异常并且任何destructor(在堆栈展开过程中)抛出任何异常时,它都会终止应用程序。但是这里只有一次从try 块中抛出异常,即来自destructor

【问题讨论】:

标签: c++ exception c++14 c++98


【解决方案1】:

自 C++11 起,没有明确说明异常规范的析构函数具有与默认生成的异常规范相同的异常规范。在您的情况下,默认生成的析构函数是noexcept(大多数默认生成的析构函数都是)​​,因此您的析构函数也被视为noexcept。从noexcept 函数抛出会自动调用std::terminate

如果您希望异常可捕获,请将析构函数声明为 throwing:

~Myclass() noexcept(false)
{
    //throw std::runtime_error("second (in destructor)");
    throw 1;
}

但在你这样做之前,重新考虑一下。这是bad idea to have throwing destructors

【讨论】:

  • noexcept() 是……很奇怪。只是noexcept 或者noexcept(true)
【解决方案2】:

从析构函数中抛出异常总是一个坏主意,因为如果已经有异常在运行(例如,在堆栈展开期间调用析构函数)std::terminate 将被调用。

也就是说,C++11 向析构函数添加了隐式 noexcept(true)(下面列出的情况除外),这意味着,如果从 noexcept(true) 析构函数抛出,则同样是 std::terminate

§12.4/3

[class.dtor] [注意:没有 noexcept 说明符的析构函数声明具有相同的异常 规范好像已经被隐式声明(15.4)。 ——尾注]

和§15.4/14

[除了.spec] 隐式声明的析构函数或没有 noexcept 说明符的析构函数的异常规范, 当且仅当它的任何潜在构造的子对象的任何析构函数是潜在抛出 可能会投掷。

我强烈建议重构您的代码,以便加入析构函数。

【讨论】:

  • std::uncaught_exception(s)来处理这种情况。 :)
猜你喜欢
  • 2012-06-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-08-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多